“React组件正在不断重新渲染。”

huangapple go评论73阅读模式
英文:

React component is continuously re-rendering

问题

// 以下是要翻译的内容:

我正在尝试在一个React组件中显示一个Google图表一个表格和一些小部件我正在使用React和redux来存储整个应用程序的状态这里是组件

我正在使用useEffect钩子在每次状态变化和页面加载时加载数据在useEffect钩子中我调用了我的loadData函数它获取了所有的数据我还有一个select组件它有featureSets的值页面上的所有图表和所有数据都依赖于该select的值当我改变这个select时它应该以新的更新呈现页面

问题是页面不断更新加载指示器一直在旋转

英文:

I'm trying to display a Google char, a table, and some widgets in a react component. I'm using React and redux to store the state of the entire app. Here I have the component:

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>
    )
}

I'm using the hook useEffect to load data at every state change and when the page loads. And on useEffect hook, I'm calling my function loadData which gets all the data. I also have a select component that has feastureSets values and all the charts and all data on that page depend on that select value. When I change that select it should render the page with new updates.

The problem is that the page is continuously updating and the loading spinner is rotating again and again.

答案1

得分: 1

在你的代码中,featureSet的值在loadData函数内更新,该函数在useEffect钩子内被调用。这会创建一个循环,其中改变featureSet会触发effect,effect调用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,以确保在这些值中任何一个发生更改时触发effect,从而按需加载数据。

英文:

In your code, the featureSet value is being updated within the loadData function, which is called inside the useEffect hook. This creates a loop where changing featureSet triggers the effect, which calls loadData, which in turn updates featureSet, and so on.

update your useEffect code in the following way:

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

In this code, I've removed featureSet from the dependency array and added token, username, and filters.url instead. This ensures that the effect is triggered when any of these values change, allowing the data to be loaded accordingly.

答案2

得分: 0

你的useEffect调用创建了一个无限循环。

这个 effect 在调用 loadData()

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

loadData 反过来又调用了 setFeatureSet,但由于你的 effect 依赖于 featureSet,而 featureSet 又被 setFeatureSet 更新,你就创建了一个循环。每次调用 loadData 都会无限重新运行 effect。

解决方法是从依赖数组中移除 featureSet

//编辑:

按照以下方式更新你的 useEffect:

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

You are creating an infinite loop with your useEffect call.

This effect is calling loadData()

useEffect(() =&gt; {
   if (token === undefined) {
      navigate(&#39;/login&#39;)
   }
   dispatch({type: &#39;ROUTE&#39;, payload: &#39;/home/key-drivers&#39;})
   loadData()
}, [featureSet /* &lt;- here is the problem */])

and loadData is in turn calling setFeatureSet but since your effect is dependent on featureSet which is being updated by setFeatureSet you are creating a loop. Every call to loadData will rerun the effect forever.

The solution would be to remove featureSet from the dependency array.

//Edit:

Update your useEffect in the following way:

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

huangapple
  • 本文由 发表于 2023年5月26日 17:00:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76339254.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定