Store data from useQuery with useState
Asked Answered
L

5

18

I'm using React hooks both to fetch GraphQL data with react-apollo and to store local state:

const [userData, setUserData] = useState({})
const { loading, error, data } = useQuery(USER_QUERY)

However, I'm wondering how to store data to userData. Is this how it's supposed to work:

useEffect(() => {
  setUserData(data)
}, [Object.entries(data).length])
Luciolucita answered 20/8, 2019 at 15:35 Comment(0)
B
28

Looks like what you have probably works. There is also a onCompleted option available in the options parameter. it takes a callback of type:

(data: TData | {}) => void

so this is another way of doing it:

const { loading, error, data } = useQuery(USER_QUERY, {onCompleted: setUserData})
Brittain answered 20/8, 2019 at 15:45 Comment(3)
I think "onCompleted" should be "onSuccess".Thaumatology
@Thaumatology at least in version 3.5.10, it is onCompletedCloutier
Why is data is not being used in onCompleted? How does it know what to set use state to?Cerement
W
10

What are you trying to do with the returned data that you are unable to accomplish by simply using it as destructured from the query hook? In most use cases it can be used immediately, as it will update itself when refetched.

If it is necessary (and it could be), as the other answer says, the useEffect hook you posted should work, but I would replace the dependency with simply data, to prevent an edge case where the response has an equal length consisting of different data and does not update:

useEffect(() => {
  setUserData(data)
}, [data])
Wabash answered 20/8, 2019 at 15:48 Comment(1)
Using useEffect in this way is not recommended because it will run only after the component is mounted resulting in a delay between loading === true and setUserData being populated. More information in the react docs beta.reactjs.org/learn/you-might-not-need-an-effectInterdict
M
4

I think something like this would work - you will need to create the initial state with useState, could be empty array and then onComplete in the useQuery would setTranscationsData... it is triggered every render when state or props change. Could of course add an inital state inside useState which insn't an empty array.

const [transactionsData, setTransactionsData] = React.useState([]);

const { error, data } = useQuery(GET_TRANSACTIONS, {
  onCompleted: () => {
    setTransactionsData(data.transactions);
  },
});

another example

  const [getLegalStatement] = useLazyQuery(GET_LEGAL_STATEMENT, {
    fetchPolicy: 'network-only',
    onCompleted: (data) => {
      setTempLegalStatement(data.getLegalStatement);
    },
    onError: () => {
      setTempLegalStatement({
        consentedLegalStatementHash: '',
        consentedSuppliersHash: '',
        statement: '',
        suppliersModal: '',
      });
      setTimeout(() => {
        setRefetchNeeded(true);
      }, 10000);
    },
  });
Monogenetic answered 3/1, 2022 at 12:12 Comment(4)
This will cause a warning: 'data' was used before it was defined.Ophthalmitis
We use this pattern pretty widely, have you tested your error? I think I wrote it correctly.Monogenetic
I'm not sure why I am seeing a warning. I copied your code exactly.Ophthalmitis
Have updated with another example, it definitely works, may be a setup thing.Monogenetic
K
2

Use onSuccess

const [userData, setUserData] = useState({})
const { data, isLoading, error } = useQuery('QueryKey', QueryFunction, { onSuccess: setUserData })

This onSuccess callback function will fire setUserData(data) for you automatically any time the query successfully fetches new data.

Kalindi answered 31/7, 2022 at 11:57 Comment(1)
This will cause a warning: 'data' was used before it was defined. There is no obvious way to get around that, so another solution would probably be better.Ophthalmitis
S
-1

To elaborate above, you can't use onSuccess/onSettled because those will not rerun if the data is cached, so if you leave the component and come back before the query expires your data won't get set.

Scincoid answered 12/1, 2023 at 0:28 Comment(2)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Iotacism
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewWellfound

© 2022 - 2024 — McMap. All rights reserved.