Reputation: 2871
Graphql provides useQuery hook to fetch data. It will get called whenever the component re-renders.
//mocking useQuery hook of graphql, which updates the data variable
const data = useQuery(false);
I am using useEffect hook to control how many times should "useQuery" be called.
What I want to do is whenever I receive the data from useQuery, I want to perform some operation on the data and set it to another state variable "stateOfValue" which is a nested object data. So this has to be done inside the useEffect hook.
Hence I need to add my stateOfValue and "data" (this has my API data) variable as a dependencies to the useEffect hook.
const [stateOfValue, setStateOfValue] = useState({
name: "jack",
options: []
});
const someOperation = (currentState) => {
return {
...currentState,
options: [1, 2, 3]
};
}
useEffect(() => {
if (data) {
let newValue = someOperation(stateOfValue);
setStateOfValue(newValue);
}
}, [data, stateOfValue]);
Basically I am adding all the variables which are being used inside my useEffect as a dependency because that is the right way to do according to Dan Abramov.
Now, according to react, state updates must be done without mutations to I am creating a new object every time I need to update the state. But with setting a new state variable object, my component gets re-rendered, causing an infinite renders.
How to go about implementing it in such a manner that I pass in all the variables to my dependency array of useEffect, and having it execute useEffect only once.
Please note: it works if I don't add stateOfValue variable to dependencies, but that would be lying to react.
Upvotes: 0
Views: 1123
Reputation: 39250
If you want to set state in an effect you can do the following:
const data = useQuery(query);
const [stateOfValue, setStateOfValue] = useState({});
const someOperation = useCallback(
() =>
setStateOfValue((current) => ({ ...current, data })),
[data]
);
useEffect(() => someOperation(), [someOperation]);
Every time data changes the function SomeOperation is re created and causes the effect to run. At some point data is loaded or there is an error and data is not re created again causing someOperation not to be created again and the effect not to run again.
Upvotes: 2
Reputation: 3488
I think you misunderstood
what you want to be in dependencies array is [data, setStateOfValue]
not [data, stateOfValue]
. because you use setStateOfValue
not stateOfValue
inside useEffect
The proper one is:
const [stateOfValue, setStateOfValue] = useState({
name: "jack",
options: []
});
const someOperation = useCallback((prevValue) => {
return {
...prevValue,
options: [1, 2, 3]
};
},[])
useEffect(() => {
if (data) {
setStateOfValue(prevValue => {
let newValue = someOperation(prevValue);
return newValue
});
}
}, [data, setStateOfValue,someOperation]);
Upvotes: 2
Reputation: 972
First I'd question if you need to store stateOfValue
as state. If not (eg it won't be edited by anything else) you could potentially use the useMemo
hook instead
const myComputedValue = useMemo(() => someOperation(data), [data]);
Now myComputedValue
will be the result of someOperation
, but it will only re-run when data changes
If it's necessary to store it as state you might be able to use the onCompleted
option in useQuery
const data = useQuery(query, {
onCompleted: response => {
let newValue = someOperation();
setStateOfValue(newValue);
}
)
Upvotes: 1