1

In my first Next.js project, I have an article component which is rendered at the server side. I'd like to fetch articls' tags from the client side because otherwise, I get too many DOM elements. So here is what I came up with:

const ArticlesPage = () => {    

    const [tags, setTags] = useState([])
    const   { isLoading, isError, data, error } = useQuery('tags', getTags, {
        onSuccess: () => setTags(data)
       }
       //...
    })
    console.log('tags are:', tags)
     return ( 
        <>
              ...
              {!isLoading && !isError &&
            <TagsComponent tags={tags} />
            
              }
              
               {isLoading && 
                 <div> Loading tags...</div>            
              }
              
              {isError && 
                 <div> Error fetching tags</div>            
              }     
         
        </>

The problem is that tags are rendered on the articles page whimsically, that is when I refresh page they do not show up but when I refocus on page, the tags are displayed. I don't see any Loading or Error being rendered either. So I'm confused what is going on here?

How can I fix this?

1
  • Does your api allow you to filter the tags, or do you need to filter them on the client side? Commented Nov 21, 2022 at 18:01

2 Answers 2

5

As mentioned in the other answer, you should not be using setState to store data that comes back from the API. Doing so will make your cache management very difficult to maintain and reason about. Instead you should use state to store the filter options for the result, and then use that state to filter the data.

If your API supports filtering

Assuming your API takes a payload to filter the response, we can store the payload we will send in a 'filter' state variable and use it for our query key.

They key function getTags should accept {queryKey: [,filter]} as its argument and should pass the filter to the api call. Then you can control it in your TagsComponent via the filter and setFilter props.

const ArticlesPage = () => {
  const [filter, setFilter] = useState({});

  const { isLoading, isError, data: tags, error } = useQuery(
    ["tags", filter], 
    getTags,
  );

  console.log("tags are:", tags);

  if(isError) return <div>Error fetching tags</div>;

  if(isLoading) return <div>Loading tags...</div>;

  return <TagsComponent 
           tags={tags} 
           filter={filter} 
           setFilter={setFilter} />;
};

If you are filtering your results locally

Since you cannot rely on the API to filter your results you'll need to filter the results once the response has returned. Again assuming you have some filter state that the user specifies, and a function filterTags(filter, tags) => filteredTags which will filter tags based on that state we can put them together in this way:

const ArticlesPage = () => {
  const [filter, setFilter] = useState({});

  const { isLoading, isError, data: tags, error } = useQuery("tags", getTags);

  const filteredTags = useMemo(() => filterTags(filter, tags), [tags, filter]);

  if(isError) return <div>Error fetching tags</div>;

  if(isLoading) return <div>Loading tags...</div>;

  return <TagsComponent 
           tags={filteredTags} 
           filter={filter} 
           setFilter={setFilter} />;
};
Sign up to request clarification or add additional context in comments.

Comments

1

You don't need to create another state just to keep track of the data, you should use the data property directly like this:

const ArticlesPage = () => {
  const { isLoading, isError, data: tags, error } = useQuery("tags", getTags);
  console.log("tags are:", tags);
  return (
    <>
      ...
      {!isLoading && !isError && <TagsComponent tags={tags} />}
      {isLoading && <div> Loading tags...</div>}
      {isError && <div> Error fetching tags</div>}
    </>
  );
};

5 Comments

Right, but in my particular case I need to make a state out of the data, because I need to use the obtained state in another component which filters out tags in a search form. How can I do that.
@blnks, It doesn't matter what the usage is, you should be able to do the same with what I posted above, I even renamed it to tags so you would see no difference there, for further details you need to create a sandbox and put your components there so we could help you better
Well if you use data: tags you get error when defining const [tags, setTags] = useState([] ). How would you define a state out of react query data?
@blnks You're missing the point. You don't need to define state out of the query data. You should be defining state out of the users choices. Then use the state (filter choice) to filter the data you show to the user..
@ChadS. What do you mean by defining state out of the users choices ?

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.