我尝试在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值,该页面上的所有图表和所有数据都依赖于该选择值。当我改变选择它应该呈现新的更新页面。
问题是页面在不断更新,加载微调器一次又一次地旋转。
2条答案
按热度按时间c6ubokkw1#
在代码中,featureSet值在loadData函数中更新,该函数在useEffect钩子中调用。这将创建一个循环,其中更改featureSet会触发效果,该效果将调用loadData,而loadData又会更新featureSet,依此类推。
按照以下方式更新useEffect代码:
在这段代码中,我从依赖数组中删除了featureSet,并添加了token、username和filters.url。这可确保在这些值中的任何一个发生更改时触发效果,从而允许相应地加载数据。
jqjz2hbq2#
您正在使用useEffect调用创建无限循环。
此效果正在调用
loadData()
而
loadData
反过来调用setFeatureSet
,但是由于你的效果依赖于featureSet
,而featureSet
是由setFeatureSet
更新的,所以你创建了一个循环。每次调用loadData
都将永远重新运行该效果。解决方案是从依赖数组中删除
featureSet
。//编辑:
按以下方式更新useEffect: