I have this following code Parent and DataFilter.
Parent.js
const Parent = () => {
const [filteredData, setFilteredData] = useState()
...
const Filter = () => {
return (
<div>
<h2>Filter your data</h2>
<DataFilter data={filteredData} updateFilter={setFilteredData} />
<Result data={filteredData} />
</div>
)
}
return (
<div>
<h1>Header</h1>
<Filter />
</div>
)
}
DataFilter.js
const DataFilter = ({ data, updateFilter }) => {
const [name, setName] = useState(defaultFilters.name)
const [age, setAge] = useState(defaultFilters.age)
...(filter logic using data)
useEffect(() => {
updateFilter({ name, age })
}, [name, age])
}
Here, I receive Maximum update depth exceeded aka infinite rendering, which I kind of know why.
Parent and DataFilter get renderedDataFilter triggers Parent's setFilteredData in useEffectParent gets rerendered because of setFilteredDataParent being rerendered triggers child component DataFilterrerenderBut, this only started happening only after I extracted Filter in 'Parent' as an inner component... I know it's weird but it was working fine when Parent was like this
const Parent = () => {
const [filteredData, setFilteredData] = useState()
...
return (
<div>
<h1>Header</h1>
<div>
<h2>Filter your data</h2>
<DataFilter data={filteredData} updateFilter={setFilteredData} />
<Result data={filteredData} />
</div>
</div>
)
}
Does anyone know why the infinite rendering happens only when it's as an inner component?
I tried using useCallback for setFilteredData but it didn't work.
Also what should I do to prevent the infinite render if I want to keep it as a component rather than directly writing everything in a return()?
Your reasoning is correct from what I can gather.
To rephrase:
The child components triggers the parent component's setFilteredData.
When state changes in the parent component the parent will re-render it's children, which will cause the <DataFilter> (along with other children) to be re-rendered, which causes the useEffect to run again since useEffect runs on every render.
This process repeats, causing the parent's state to update and again re-render. And this continues in an infinite loop.
The reason it worked with with your previous structure I believe was due to the nesting of the components, with the parents changes only updating relevant children (as opposed to the longer chain of nesting like your current setup).
If you want it not to always re-render, I'd either keep the relevant logic in the child so the child is responsible for only its own state and re-renders, instead of the parent state changes causing the child to re-render.
Alternatively, I would look at wrapping the child in React.memo HOC which will limit re-renders depending on the props.