Search code examples
react-nativereact-hookstanstackreact-query

React Query useQuery() and useState()


I have a screen in a React Native app, where I fetch some data using useQuery , save this data in a object state with setState , then update the state values inside child components. I am stuck at the initialisation of state after the data is fetched. This could have been a lot easier with the now-deprecated onSuccess hook, but I would like to know if there are better alternatives.

I am using React 18 ,React Native 0.72, and React Query v5.

I tried the following things:

  1. The select prop of useQuery:
const {refetch, data, isLoading, error} = useQuery({
    queryKey: ['leadPreferences'],
    queryFn: async () => getLeadPreferences(setLoggedIn),
    select: (res) => {
      if (res != null) {
        const leadPreferencesData = res.body;
        console.log('running this: ', i++);
        return updateLeadPreferences({
          destinations: [],
          whatsAppContact: leadPreferencesData.whatsapp_contact,
          whatsAppCommStatus: leadPreferencesData.whatsapp_enabled,
          leadEmail: leadPreferencesData.alert_email_ids,
          emailAlertStatus: leadPreferencesData.email_alert_enabled,
          emailAlertTimes: [],
          leadTypes: leadPreferencesData.lead_types,
          packageTypes: leadPreferencesData.package_types,
          defaultHomePage: leadPreferencesData.landing_page,
        });
      }
    },
  });

This throws the data is undefined error.

2. useEffect method both before and after the useQuery call:

useEffect(() => {
    if (data && data != undefined) {
      const leadPreferencesData = data.body;
      console.log('running this: ', i++);
      updateLeadPreferences({
        destinations: [],
        whatsAppContact: leadPreferencesData.whatsapp_contact,
        whatsAppCommStatus: leadPreferencesData.whatsapp_enabled,
        leadEmail: leadPreferencesData.alert_email_ids,
        emailAlertStatus: leadPreferencesData.email_alert_enabled,
        emailAlertTimes: [],
        leadTypes: leadPreferencesData.lead_types,
        packageTypes: leadPreferencesData.package_types,
        defaultHomePage: leadPreferencesData.landing_page,
      });
    }
  },);

What should happen:

whenever the value of data changes, the state must get updated. Issues: Throwing "cannot read properties of undefined" -> if useEffect is called before the useQuery hook "Rendered more hooks than during the previous render" -> if the useEffect is called after useQuery, with data as the dependency array.

  1. Calling the updateLeadPreferences just before the UI is returned. This results in about 51 renders before the debugger throws "too many re-renders" error.

Solution

  • Can you try this once and tell me? (as below)

    Use useQuery to fetch your data without attempting to set the state directly within the select function. The select option should be purely for selecting or transforming the data you want to use in your component, not for side effects like updating state. Utilize useEffect to watch for changes in the data returned by useQuery and then update your component's state accordingly.

    //small example in line with your code
    import React, { useState, useEffect } from 'react';
    import { useQuery } from 'react-query';
    
    const initialLeadPreferencesState = {
      destinations: [],
      whatsAppContact: '',
      whatsAppCommStatus: false,
      leadEmail: '',
      emailAlertStatus: false,
      emailAlertTimes: [],
      leadTypes: [],
      packageTypes: [],
      defaultHomePage: '',
    };
    
    const YourComponent = () => {
      const [leadPreferences, setLeadPreferences] = useState(initialLeadPreferencesState);
    
      const { data, isLoading, error } = useQuery('leadPreferences', async () => {
        const response = await getLeadPreferences();
        return response.body;
      });
    
      useEffect(() => {
        if (data) {
          const leadPreferencesData = data;
          setLeadPreferences({
            ...leadPreferences,
            whatsAppContact: leadPreferencesData.whatsapp_contact,
            whatsAppCommStatus: leadPreferencesData.whatsapp_enabled,
            leadEmail: leadPreferencesData.alert_email_ids,
            emailAlertStatus: leadPreferencesData.email_alert_enabled,
            leadTypes: leadPreferencesData.lead_types,
            packageTypes: leadPreferencesData.package_types,
            defaultHomePage: leadPreferencesData.landing_page,
          });
        }
      }, [data]); // Dependency array ensures this effect runs only when `data` changes
    
      if (isLoading) return <div>Loading...</div>;
      if (error) return <div>An error occurred</div>;
    
      return (
        <div>
          {/* Render your UI here using `leadPreferences` state */}
        </div>
      );
    };