英文:
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(() => {
if (token === undefined) {
navigate('/login')
}
dispatch({type: 'ROUTE', payload: '/home/key-drivers'})
loadData()
}, [featureSet /* <- 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(() => {
if (token === undefined) {
navigate('/login');
}
dispatch({ type: 'ROUTE', payload: '/home/key-drivers' });
loadData();
}, [token, username, filters.url]);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论