0

I'm trying to understand useContext with what, I thought, would be a trivial example...

I'm not putting the entire code in here but only relevant snippets, the full code is on codesandbox

I have a page with two components. On the left is a list. On the right is a container. When I click on an "add" button under my items, they appear on the component container on the right.

After that, one can click on a submit button in the list container which routes to another component with the list of items. One can remove items from there.

I'm doing all to learn how to share properties all over a React app. I have a few problems:

  1. When I add an element, if the name is already included. I don't want to display it ten times. Instead, I want to display it once and then show how many times it's been added next to it. I struggle to do that because of how setItemList works in here:
  const addItem = useCallback((item) => {
    setCounter((prev) => ({
      ...prev,
      [item.name]: (prev[item.name] || 0) + 1
    }));
    setItemList((prev) => [...prev, item]);
  }, []);

And that's how I add in my List component:

  const addItemToItemList = (item: Item) => {
    /*if (!itemList.includes(item))*/ addItem(item);
  };

I commented the if statement out to be able to add an item more than once but then it appears over and over again instead of just once (with the counter next to it)

  1. My second problem is I don't know how to access the second counter. I don't even know how to access it with a simple console.log...

  2. Finally, at the moment, if I add the same thing three times in my list container and then submit, on the last page, when I click remove, it removes all of them at once whereas I want to delete only one obviously. I guess it's because my removeItem works with the name but I can't think of any other way to implement that...

  const removeItem = useCallback(
    (itemName) =>
      setItemList((prev) => prev.filter((it) => it.name !== itemName)),
    []
  );

Anyway, I look forward to getting some tips and hints. Enjoying the React Hooks journey so far. Thank you!

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
NiHaoLiHai
  • 23
  • 1
  • 8

2 Answers2

0

There are a few architectural issues at play here:

First of all is that if you want to count the number of times an item has been added to the list but don't want to add it multiple times, you cannot do the check when calling addItem() since you also increase the counter inside that function. Instead, you should check for duplicates in setItemList():

setItemList((prev) => prev.includes(item) ? prev : [...prev, item])

Second, I don't understand where do you want to access the second counter, if you mean in the container next to the item.name then, it's as easy as just getting it from the context like you did with itemList:

const { itemList, counter } = useContext(ItemListContext);

// ...
<li key={i}>{counter[item.name]} - {item.name}</li>
// ...

For your third point, you'll want to use the counter inside the method to decrease it every time it's called and only remove the item if counter == 0. Don't forget to add counter to the list of dependencies of useCallback as well, or you'll always be working with an obsolete value.

Lars
  • 554
  • 1
  • 5
  • 19
  • Thank you so much for your clear and precise answer. That's exactly what I was trying to do. And spot on for the counter in the list of dependencies, I was actually going to forget that. – NiHaoLiHai Apr 10 '21 at 11:56
0

I edited your sandbox here to achieve the desired result, using a similar implementation and a count array, incrementing it when an item is added

Josh
  • 827
  • 5
  • 7
  • Thank you for your answer. count had to be changed to counter and it had to be `counter[item.name]` for it to count for each individual item. Thanks again! – NiHaoLiHai Apr 10 '21 at 11:58