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.
2
Answers
You are creating an infinite loop with your useEffect call.
This effect is calling
loadData()
and
loadData
is in turn callingsetFeatureSet
but since your effect is dependent onfeatureSet
which is being updated bysetFeatureSet
you are creating a loop. Every call toloadData
will rerun the effect forever.The solution would be to remove
featureSet
from the dependency array.//Edit:
Update your useEffect in the following way:
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:
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.