I want to fetch data on a Child component and display an Activity Indicator on its parent until the data is loaded.
This is an simplified example, in reality I would have multiple data fetching components as Children, each being able to set the overall “loading” state, which is why I want to hold the “loading” state on the Parent:
Parent.jsx
const Parent = () => {
const [value, setValue] = useState(0)
const [loading, setLoading] = useState(false)
const [unchangedState, setUnchangedState] = useState("I am the same!")
return (
<View style={{height: "100%", justifyContent: "center", alignItems: "center", rowGap: 20}}>
{loading
? <Text>Loading...</Text>
: <View>
<Text>Parent Component: Value {value}</Text>
<Child setValue={setValue}
setLoading={setLoading}
unchangedState={unchangedState}
key="first"/>
</View>
}
</View>
);
};
Child.jsx
const DATA = {
"first": "Item 1",
"second": "Item 2",
"third": "Item 3",
}
const Child = ({setValue, setLoading, unchangedState, dataKey}) => {
const [data, setData] = useState(DATA[dataKey])
console.log("Child rendered!")
useEffect(() => {
setLoading(true)
setData(DATA[dataKey])
setTimeout(() => setLoading(false), 3000)
}, [dataKey]);
return (
<View style={{justifyContent:"center", alignItems:"center"}}>
<Button title="Increase Value"
onPress={() => setValue((prev) => prev + 1)}>
</Button>
<Text>{unchangedState}</Text>
<Text>{data}</Text>
</View>
);
};
export default memo(Child);
Maybe my understanding of memo
is incorrect, but I do not quite get why the Child component gets continuous re-rendered.
-
I am passing the props
setValue
,setLoading
,unchangedState
andkey
down to the child.
setState functions seem to be identical between renders. Wrapping them in useCallback did not solve my issue as well.
All of those props are unchanged, which per my understanding, would tellmemo
to not re-render the component -
The
useEffect
hook only takesdataKey
as dependency, which stays constant and therefore this should only be executed on the initial mount
I would understand my code to
-
Render the Parent with
loading=false
as initial state, rendering the Child component -
This should trigger the initial
useEffect
execution, settingloading
to true, showing the “Loading…” component for a second, before settingloading
to false again -
Afterwards, React memoizes the component as all props are unchanged and re-renders it without re-executing its
useEffect
Instead it directly jumps into an infinite loop, just displaying “Loading…”
Where is my understanding incorrect? Why is it continuous re-rendering? How should I structure my code to prevent it, while still being able to pass props to children, executing (API) requests on the children but let the parent decide to render a Loading screen until the data is available.