useState Array of Objects
Asked Answered
C

3

14

I have a search box that I can copy and paste a column from excel. I parse the input and generate an array of the entries.

I am then mapping over the entries and calling a custom hook with each item to fetch some data from my graphql endpoint.

For example:

3 entries are provided from the search box:

["D38999/26", "LJT06RE25-61PA", "D38999/46FJ61AA"]

The fetchData function receives one of these items at a time as the query parameter. Currently, this process would return 3 separate objects as such:

{query: "D38999/26", cables: Array(35)}
{query: "LJT06RE25-61PA", cables: Array(1)}
{query: "D38999/46FJ61AA", cables: Array(1)}

How do I set up a react hook to allow me to append each object into the result State as an array of objects?

My end goal would be an array of objects like this:

[
{query: "D38999/26", cables: Array(35)},
{query: "LJT06RE25-61PA", cables: Array(1)},
{query: "D38999/46FJ61AA", cables: Array(1)}
]

This is my current custom hook to resolve my API endpoint

const useCableMatch = () => {
  const [result, setResult] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const client = useApolloClient();

  const fetchData = async query => {
    setIsError(false);
    setIsLoading(true);
    try {
      const { data } = await client.query({
        query: GET_VALID_CABLE,
        variables: { pn: `%${query}%` },
      });

      const response = { query, ...data };
      console.log('response:', response);

      setResult(response);
    } catch (error) {
      setIsError(true);
    }
    setIsLoading(false);
  };

  return [{ result, isLoading, isError }, fetchData];
};

Currently setResult is set up to only return a single object from response. I would like to return an array with each object generated appended to the existing array of objects.

Colicweed answered 2/9, 2019 at 0:8 Comment(2)
Maybe with deconstruction? setResult([...result, response])Handclasp
React functional components must be pure functions of props. Just like any state has to be wrapped in a call to useState, any side effects must be wrapped in a call to useEffect. Or just use a class component.Christine
B
32

Assuming that response can be added directly to result array, you can:

setResult(result => [...result, response]);

This will append the new response from the previous result state and by using array spread operator.

Bibliopole answered 2/9, 2019 at 2:45 Comment(5)
This worked! I'm curious why setResult(result => [...result, response]); But this does not.. setResult([...result, response]);Colicweed
@JeremyLondon for setting the state is asynchronous. That notation is making sure to use the previous state of result.Bibliopole
upvoted. also if you update some value deep in result and need a rerender you can just do setResult( result => [...result] ). consider it a nudge to cause a renderConfluent
Thanks for this. I was struggling with why my child component would not update when I was using .push() to add to the existing state array and trying to set that in its state. It would work but my child component depending on the array was not immediately re-rendering as expected. (It would on a subsequent render or a force render).Anomie
Well hell I learned something new today. Thanks for that.Higley
H
2

You could just pass the entire array into the hook and return the result as an array. You should also use useEffect for async logic. I rewrote your code to process all 3 fields at once:

const useCableMatch = (searchInput) => {
  const [result, setResult] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const client = useApolloClient();

  useEffect((searchInput) => {
     setIsError(false);
     setIsLoading(true);
     let response
     for(let field of searchInput){
        try {
           const { data } = await client.query({
              query: GET_VALID_CABLE,
              variables: { pn: `%${field}%` },
           });

           response = { ...data };
         } catch (error) {
             setIsError(true);
         }
     }
     setResult(current => {query, ...current, ...response);
     setIsLoading(false);
   };
  }, [searchInput])

  return [{ result, isLoading, isError }];
};
Hardshell answered 2/9, 2019 at 2:35 Comment(0)
J
1

From React docs Updating Arrays in State, we can do this

setResult([...result, response])
Jaehne answered 20/2, 2023 at 15:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.