1
votes

I have a component with an array of objects, which among other things i am filtering based on a string. Problem is when I try to set the return of this filter to the local state, it's throwing errors that i am not quite understanding the reason.

import React, { useState, useEffect } from 'react';
import { useQuery } from '@apollo/react-hooks';
import gql from 'graphql-tag'
const ProductsGrid = () => {


const [productsList, setProductsList] = useState([]);
  const { loading, data } = useQuery(GET_PRODUCTS);
  if (loading) return <div><h4>bla bla bla</h4></div>
  const { merchants } = data;
  let filtered = [];
  merchants.map(merchant => {
    merchant.products.map(product => {
      if (product.name.includes('Polo')) {
        filtered.push(product);
      }
    })
  })
  console.log({ filtered });
}

This is printing the following: enter image description here

So, because I want this array in my state, I decided to do this: setProductsList(filtered); and what happened after inserting this line was this:

enter image description here

It started rendering multiple times. I assumed that, every time the state changes, it re-renders the component (correct me if im wrong). I don't know why it did it multiple times though.

So, I thought on using useEffect to achieve the expected behaviour here.

useEffect(() => {
console.log('useeffect', data);
if (data) {
  const { merchants } = data;
  console.log({merchants })
  let filtered = [];
  merchants.map(merchant => {
    merchant.products.map(product => {
      if (product.name.includes('Polo')) {
        filtered.push(product);
        // console.log({ filtered });
      }
    })
  })
  console.log({ filtered });
  setProductsList(filtered);
}

}, [data])

and the output was it: enter image description here

So, I am understanding what's going on here and what is this last error about. I assume my approaching is towards the right direction, using useEffect to run the function only once.

1
show full component code .adel
@adel this is the full code. Apart from what you see here, there's only a return <div> abc</div> which is irrelevant.vbotio
Your question may contain the full code, but it's piecemeal - we don't know how the useEffect fits into everything else. It would be helpful if you could show the full component, or at least enough of it so we can see all hook usages in situ.backtick
@backtick jsfiddle.net/4v2u10yr/1 the entire component script here.vbotio

1 Answers

0
votes

Your problem is due to the useEffect call occurring after the if (loading) condition, which returns early.

Calling hooks after a conditional return statement is illegal as it violates the guarantee that hooks are always called in exactly the same order on every render.

const { loading, data } = useQuery(GET_PRODUCTS);
const [productsList, setProductsList] = useState([]);
if (loading)
  return (
    <div>
      <h4>bla bla bla</h4>
    </div>
  );  // <-- Cannot use hooks after this point
useEffect(/* ... */)

To fix, move the useEffect call to be before the conditional.