0

I pull data with react-query and need to store it in state due to some form editing that is happening later.

Before the form editing, it worked well:

import { useQuery } from '@apollo/client';
import { SINGLE_PARTICIPANT_QUERY } from 'queries/participantQueries';
import { ProfileGeneral } from './ProfileGeneral';


const ProfilePage = ({ id }) => {

    const {data, loading, error} = useQuery(SINGLE_PARTICIPANT_QUERY, {
        variables: {
            id
        }
    });

    if (loading) {
        return <div>Loading</div>;
    }
    if (error) {
        return (
            <div>
                {error.message} />
            </div>
        );
    }
    const { participant } =data;
    return (
        <div>
           <ProfileGeneral participant={participant} />
        </div>

But after trying to add it into state, I keep getting an error message, indicating that it renders without having the data ready.

import { useQuery } from '@apollo/client';
import { SINGLE_PARTICIPANT_QUERY } from 'queries/participantQueries';
import { ProfileGeneral } from './ProfileGeneral';
import { useEffect, useState } from 'react';

const ProfilePage = ({ id }) => {
    const [participant, setParticipant] = useState(null);
    const { data, loading, error } = useQuery(SINGLE_PARTICIPANT_QUERY, {
        variables: {
            id
        }
    });

    useEffect(() => {
        if (data && data.participant) {
            setParticipant(data.participant);
        }
    }, [data, participant]);

    if (loading) {
        return <div>Loading</div>;
    }
    if (error) {
        return (
            <div>
                {error.message} />
            </div>
        );
    }

    return (
        <div>
           <ProfileGeneral participant={participant} />
        </div>

I get back:

Server Error
TypeError: Cannot read properties of null (reading 'firstName')

This error happened while generating the page. Any console logs will be displayed in the terminal window.

I know that I need to make it wait or re-render as soon as it has the data from the query, but I am not sure how to prevent it.

Thank you for taking a look!

2 Answers 2

2

It because:

  1. setParticipant change state asynchronously,
  2. useEffect invokes after render actually happend

so even if data.participant is not empty, participant is, until next render phase

You could change to this:

const ProfilePage = ({ id }) => {
    //...

    if (loading || !participant) {
        return <div>Loading</div>;
    }

    //...
}
Sign up to request clarification or add additional context in comments.

1 Comment

I knew it was something related to timing, but wasn't able to figure it out. This is a super straight forward explanation, thank you!
2

An option is to split the component into two. One is responsible for fetching (useQuery) and one for form editing (useState) so that the parent component provides initial state. A downside to this approach is that until the query loads, you don't see the form (unless you make "a form placeholder").

Example:

function Fetcher() {
  const {data, isError, isLoading} = useQuery(...)

  if (isLoading) {
    return <Loader />
  }

  if (isError) {
    return <p>Oops, failed to load</p>
  }

  return <Form initialState={data} />
}

function Form({ initialState }) {
  const [state, setState] = useState(initialState)

  return <form>...</form>
}

Now you have a local copy of the data in Form's state and no longer have any async issue. Form simply doesn't render until you have data. If you want a better visual, you can replace Loader with anything!

In case you need to propagate changes (meaning that if the query re-runs and fetches fresh data), you can use a key to reset the Form as well. Use this with caution because background sync would clear the form even if the user was just typing something, so you need to play with the options of such query.

  const {data, isLoading, isError, dataUpdatedAt} = useQuery(...)
 
  ...

  return <Form key={dataUpdatedAt} initialState={data} />

1 Comment

Thank you so much for the proposed alternative and the shortcomings/benefits of it! Truly appreciate it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.