Reputation: 83
so I have this code
const [localState, setLocalState] = useState<StateType[]>([]);
const { data = { attribute: [] }, loading } = useQuery<DataType>(QUERY, {
variables: {
id: client && client.id
},
skip: user.clients && user.clients.length === 0
});
useEffect(() => {
if (loading || !data) {
return undefined;
}
if (data && data.attribute) {
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
}
}, [data]);
the issue is when useQuery returns empty(undefined) result and data defaults to {attribute: []}
useEffect keeps being triggered for forever, however when useQuery returns data (so it is not defaulted) useEffects is being entered only once. The solution for this problem was only to remove default parameter = {attribute: []}
in the query so it looks like this:
const [localState, setLocalState] = useState<StateType[]>([]);
const { data, loading } = useQuery<DataType>(QUERY, {
variables: {
id: client && client.id
},
skip: user.clients && user.clients.length === 0
});
useEffect(() => {
if (loading || !data) {
return undefined;
}
if (data && data.attribute) {
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
}
}, [data]);
Why defaulting parameter in useQuery makes useEffect being triggered for infinity?
(Important note to add - I tried to remove sort function, thinking that it mutates data object and causes reentering but it didn't change anything)
Upvotes: 2
Views: 880
Reputation: 8418
const { data = { attribute: [] }, loading } = useQuery<DataType>(QUERY, {
This declaration doesn't make a larger sense:
data
checking first;It can create a new data
object on every render ... but it shouldn't cause infinite rerenderings ... some other reasons?
You can break the effect loop by:
const [localState, setLocalState] = useState(null);
useEffect(() => {
if (data && data.attribute && !localState) {
You can also use onCompleted
event in hook:
const { loading } = useQuery<DataType>(QUERY, {
variables: {
id: client && client.id
},
skip: user.clients && user.clients.length === 0
onCompleted: (data) => {
if(data && data.attribute) {
const sortedResult = data.attribute.sort((a, b) =>
a.updatedAt < b.updatedAt ? 1 : -1
);
setLocalState(sortedResult);
}
}
});
... check/use loading
and {localState && <SomeResultView data={localState} />}
Upvotes: 0
Reputation: 3368
Your data
is a new object at each render, as such re-triggering your useEffect
that re-triggers an render and so on.
I don't think you need a useEffect
here:
const { data, loading } = useQuery<
DataType
>(QUERY, {
variables: {
id: (client && client.id)
},
skip: user.clients && user.clients.length === 0
});
const localState = (!loading && data && data.attribute)
? undefined
: data.attribute.sort(
(a, b) => (a.updatedAt < b.updatedAt ? 1 : -1)
);
should be enough
Upvotes: 2