javascript React组件不断重新渲染

6uxekuva  于 2023-05-27  发布在  Java
关注(0)|答案(2)|浏览(173)

我尝试在react组件中显示一个Google char、一个表和一些小部件。我使用React和Redux来存储整个应用的状态。这里我有一个组件:

import { useEffect, useState } from "react";
import Select from 'react-select';
import { useDispatch, useSelector } from "react-redux";
import { useNavigate } from "react-router-dom";
import { updateFilters } from "../../../services/FiltersService";
import { buildQueryStringFromObject, listHeaders } from "../../../utils";
import FeedCheckFilter from "../../Commons/Filters/Filter";
import filtersProp from "../../Commons/Filters/FiltersProperties";
import Header from "../../Commons/Header";
import LoadingSpinner from "../../Commons/LoadingSpinner";
import { 
    createWidget, 
    deleteWidget, 
    editWidget, 
    getFeatures, 
    getFeaturesChartData 
} from "../../../services/KeyDriversService";
import { KeyDriversChart } from "./KeyDriversChart";
import KeyDriversTable from "./KeyDriversTabel";
import KeyDriversWidgets from "./KeyDriversWidgets";
import KeyDriversForm from "./KeyDriversForm";
import './KeyDrivers.css';

export default function KeyDrivers() {
    const dispatch = useDispatch()
    let navigate = useNavigate();
    const channels = useSelector((state) => state.channels.channels)
    const groups = useSelector((state) => state.groups.groups)
    const products = useSelector((state) => state.products.products)
    const filters = useSelector((state) => state.filters.filters)
    const token = useSelector((state) => state.user.profile.token) 
    const username = useSelector((state) => state.user.profile.auth)
    let featureSets = useSelector((state) => state.keyDrivers.featureSets)
    const [isLoading, setIsLoading] = useState(true)
    const [keyDriverTableData, setKeyDriverTableData] = useState([])
    const [showDeleteModal, setShowDeleteModal] = useState(false);
    const [showCharts, setShowCharts] = useState(true)
    const [featureSet, setFeatureSet] = useState()
    const listHeader = listHeaders

    const loadData = async (queryUrl = filters.url) => {
        setIsLoading(true)

        let featureSetId = undefined
        if (featureSet) {
            featureSetId = featureSet.id
        } else {
            featureSetId = featureSets[0].id;
        }

        let actionKeyDrivers = await getFeatures({token, username, queryUrl, featureSetId}) 
        setFeatureSet({
            label: actionKeyDrivers.payload[0].featureSet.name, 
            value: actionKeyDrivers.payload[0].featureSet.name, 
            id: actionKeyDrivers.payload[0].featureSet.id
        })
        dispatch(actionKeyDrivers)

        if (featureSetId) {
            let actionCartData = await getFeaturesChartData({token, username, queryUrl, featureSetId}) 
            setShowCharts(true)
            setKeyDriverTableData(actionCartData.payload)     
        } else {
            setShowCharts(false)
        }
       
        setIsLoading(false)
    }

    // I got this pattern from this example: 
    // https://isotropic.co/how-to-fix-the-useeffect-must-not-return-anything-besides-a-function-warning/

    useEffect(() => {
        if (token === undefined) {
            navigate('/login')
        }
        dispatch({type: 'ROUTE', payload: '/home/key-drivers'})
        loadData()
    }, [featureSet])

    const changeFilters = (changedFilters) => {
        let queryUrl = buildQueryStringFromObject(changedFilters);
        changedFilters['url'] = queryUrl;

        // changeFilters:filters
        let filtersAction = updateFilters({changedFilters});
     
        dispatch(filtersAction);
        loadData(queryUrl);
    }

    const changeSelectFeatureSet = (val) => {
        setFeatureSet(val)
    }

    const handleDeleteWidget = async (widgetId) => {
        let action = await deleteWidget({token, username, widgetId})
        dispatch(action)
        loadData(filters.url)
        setShowDeleteModal(false)
    }

    const handleEditWidget = async (widget) => {
        let action = await editWidget({token, username, widget})
        dispatch(action)
        loadData(filters.url)
    }

    const handleCreateWidget = async (inputs) => {
        inputs = {
            ...inputs,
            'featureSet' : {
                'id': featureSet.id
            }
        }
        let action = await createWidget({token, username, inputs})
        dispatch(action)
        loadData(filters.url)
    }

    const filterProps = filtersProp(filters, products, groups, channels)

    featureSets = featureSets && featureSets.map((featureSet) => {
        return { label: featureSet.name, value: featureSet.name, id: featureSet.id }
    })

    return (
        <div>
            <Header listHeader={listHeader} mainHeader={false} />
            <FeedCheckFilter
                productItemName={filterProps.productItemName}
                productDefault={filterProps.productDefault}
                product={filterProps.product}
                productOptions={filterProps.productOptions}
 
                username={username}
                isLoading={isLoading}

                onChange={changeFilters}
            />
            {isLoading ? 
                <LoadingSpinner /> 
                : 
                <>
                    <div className="featureSet-select">
                        <Select 
                            value={featureSet} 
                            options={featureSets} 
                            onChange={changeSelectFeatureSet} 
                            />
                    </div>
                    {showCharts ? 
                        <>
                            <KeyDriversChart 
                                data={keyDriverTableData ? keyDriverTableData : []} 
                                featureSet={featureSet}
                                selectedProduct={filterProps.product}
                            />
                            <KeyDriversTable featuresData={keyDriverTableData} />
                            <KeyDriversWidgets 
                                data={keyDriverTableData} 
                                show={showDeleteModal} 
                                setShow={setShowDeleteModal} 
                                deleteHandler={handleDeleteWidget}
                                editHandler={handleEditWidget}
                                filters={filters}
                            />
                        </>
                        :   
                        <>
                            <h1 className="driver-missing">There are no drivers defined!</h1>
                        </> 
                    }
                    
                    <KeyDriversForm handleClick={handleCreateWidget}/>
                </>
            }
        </div>
    )
}

我使用钩子useEffect在每次状态更改和页面加载时加载数据。在useEffect钩子上,我调用函数loadData来获取所有数据。我还有一个选择组件,它具有feastureSets值,该页面上的所有图表和所有数据都依赖于该选择值。当我改变选择它应该呈现新的更新页面。
问题是页面在不断更新,加载微调器一次又一次地旋转。

c6ubokkw

c6ubokkw1#

在代码中,featureSet值在loadData函数中更新,该函数在useEffect钩子中调用。这将创建一个循环,其中更改featureSet会触发效果,该效果将调用loadData,而loadData又会更新featureSet,依此类推。
按照以下方式更新useEffect代码:

useEffect(() => {
    if (token === undefined) {
        navigate('/login');
    }
    dispatch({ type: 'ROUTE', payload: '/home/key-drivers' });
    loadData();
}, [token, username, filters.url]);

在这段代码中,我从依赖数组中删除了featureSet,并添加了token、username和filters.url。这可确保在这些值中的任何一个发生更改时触发效果,从而允许相应地加载数据。

jqjz2hbq

jqjz2hbq2#

您正在使用useEffect调用创建无限循环。
此效果正在调用loadData()

useEffect(() => {
   if (token === undefined) {
      navigate('/login')
   }
   dispatch({type: 'ROUTE', payload: '/home/key-drivers'})
   loadData()
}, [featureSet /* <- here is the problem */])

loadData反过来调用setFeatureSet,但是由于你的效果依赖于featureSet,而featureSet是由setFeatureSet更新的,所以你创建了一个循环。每次调用loadData都将永远重新运行该效果。
解决方案是从依赖数组中删除featureSet
//编辑:
按以下方式更新useEffect:

useEffect(() => {
    if (token === undefined) {
        navigate('/login');
    }
    dispatch({ type: 'ROUTE', payload: '/home/key-drivers' });
    loadData();
}, [token, username, filters.url]);

相关问题