1

I have an array, which is given as prop into my component named Child. Now, every time a new item is added to the array a fetch against an API should be made.

This array is held in a component named Parent using the useState hook. Whenever I want to add a new item, I have to recreate the array, since I'm not allowed to mutate it directly.

I tried to simplify my use case in the following code snippet:

const Parent = () => {
    const [array, setArray] = useState([]);

    ///...anywhere
    setArray(a => [...a, newItem]);

    return <Child array={array} />;
}

const Child = ({ array }) => {
    useEffect(() => {
        array.forEach(element => {
            fetch(...);    
        });
    }, [array]);

    return ...;
}

Now, my question is: How can I achieve to fetch new data from my API only for the new element but not for the whole array again?

I hope I described my issue good enough. If anything is unclear or missing, let me know.

2
  • 1
    your child component should receive only those items which need to be fetched Commented Aug 23, 2019 at 7:03
  • @tklepzig updated my answer with a second alternative in case you want to keep your existent structure. Commented Aug 23, 2019 at 18:35

2 Answers 2

1

How about instead fetching the API data in Parent and just passing the end result to Child? That refactoring would provide some benefits:

  • Parent owns the items array state and knows when and where a new item is added. That makes an incremental fetch very easy. You also get division of container and presentational components for free.
  • The fetched API data is related to the items array. You probably want to use them together in some way (save api data as state, combine them, etc.). This constellation would promote derived state, which is a more error prone pattern.

Something like following example could already do what you want - add an item via onClick (or somehow else), fetch its data and pass the whole array down to Child:

const Parent = () => {
  const [array, setArray] = useState([]);

  return (
    <div onClick={addItem}>
      <Child array={array} />;
    </div>
  );

  function addItem(e) {
    const item = createItemSomehow(...)
    fetch(...).then(data => setArray([...array, { item, data }]));
  }
};

Update:

If you want to keep your structure and API as is, an alternative would be to memoize the previous arrays prop in your Child with a usePrevious hook and look for item changes.

const Child = ({ array }) => {
  const prevArray = usePrevious(array);
  useEffect(() => {
    if (array !== prevArray && array.length) {
      //fetch(...)
      console.log(`fetch data for index ${array.length - 1}`);
    }
  });
  return (...);
};

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

Codesandbox

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

1 Comment

Thanks a lot, I'll have a look into it!
0

You could, for example, keep a list of previously fetched items.

const Child = ({ values }) => {

  const [fetched, setFetched] = useState([]);

  useEffect(() => {
    values.forEach(v => {
      if (!fetched.includes(v)) {
        setFetched(fetched => [...fetched, v]);
        fetch(v);
      }
    });
  }, [values, logged]);

https://codesandbox.io/s/affectionate-colden-sfpff

Comments

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.