React useEffect dependent on value which is initialized after api has been called

Viewed 335

My api returns an object of the following type:

type TApiReturn = {
  a: string;
  b?: string;
  c?: string;
  d?: string;
};

Now, after having received the data, I want to pass part of it through to a child component.

type TChildComponent = {
a: string;
c?: string;
};

What I thought would work:

const [allData, setAllData] = React.useState<TApiReturn>();
const [childData, setChildData = React.useState<TChildComponent>();

useEffect(() => {
    const fetchData = async () => {
      const result = await api.get('myDataPath'); //this works, not part of it
      setData(result.data);
    };

    fetchData();
  }, []);

and then, to update the child component data:

useEffect(() => {
    let parsedData: TChildComponent;
    parsedData.a = data.a;
    
    if (data.c) {
      parsedData.c = data.c;
    } 
  
    setChildData(parsedData);
  }, [allData.a]);

But yeah, this cannot work, since at the Start, allData.a is undefined at the beginning.

So to sum it up: I am unsure how to approach this situation. I understand that hooks do not have a callback function on purpose. But this situation seems to be quite normal, so there has to be a better design pattern to tackle this issue which I am not seeing.

1 Answers

Since state should be immutable in React, instead of having a dependency array of allData.a, having just a dependency of allData could work (and then you can check if allData exists, and if it does, to compare allData.a against childDate.a).

But I think a better approach would be not to split the same data structure into multiple stateful variables, and just pass down allData.a when it exists, eg:

const [allData, setAllData] = React.useState<TApiReturn>();
// no childData at all

// ...

return (
  <Child a={allData?.a} c={allData?.c} />
);
Related