DEV Community

Cover image for Custom React Hooks: useArray

Custom React Hooks: useArray

Ludal 🚀 on October 31, 2021

Another week, another custom React hook for your hooks backpack. In this episode, we'll implement the useArray hook to make arrays management easie...
Collapse
 
ericbdev profile image
Eric

Nice simplification for the general create/delete -- but what about a simple update?

In a larger context I often see mistakes when in a cooersive/mutable case like react+js. For some people adding splice/slice into a case useArray might not come natural.

Collapse
 
iamludal profile image
Ludal 🚀

What about this? (in O(n), but in all cases, I guess we have no other choice, since making a copy of the original array to make the replacement in O(1) would already be in O(n) too)

const update = (index, newElement) => {
  setValue(oldValue => oldValue.map((element, i) => (
    i === index ? newElement : element
  )))
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
op profile image
op

Immer (and its useImmer hook) can be a lifesaver when dealing with arrays, especially nested ones. It lets you write much simpler update logic while managing all the immutability for you.

I made a similar todo example here using it (although a normal useState realistically would have sufficed): codesandbox.io/s/magical-ride-9l8d...

beta.reactjs.org/learn/updating-ob...

Collapse
 
iamludal profile image
Ludal 🚀

Interesting, thanks for sharing. But this looks like the default useState way to deal with arrays, doesn't it? (With just your example, I don't really understand the benefits of using it.)

Thread Thread
 
op profile image
op • Edited

I agree it's not a very good example for showing the benefits of Immer, but basically it just allows you to write "mutating" syntax while not actually mutating the data. Try recreating my toggleCompleted function with useState and you'll see the difference.

Take this slightly more complex data structure:

const [product, setProduct] = useState({
  name: "Sneaker",
  stock: [
    {
      id: 1,
      store: "Store 1",
      quantity: 10
    },
    {
      id: 2,
      store: "Store 2",
      quantity: 20
    }
  ]
});
Enter fullscreen mode Exit fullscreen mode

If you wanted to update the quantity of a stock item with useState you would maybe do something like this:

setProduct({
  ...product,
  stock: product.stock.map((s) => {
    if (s.id === store.id) {
      return { ...s, quantity: s.quantity - 1 };
    }
    return s;
  })
});
Enter fullscreen mode Exit fullscreen mode

With useImmer you can instead do this:

setProduct((product) => {
  const s = product.stock.find(s=>s.id === store.id);
  s.quantity--;
});
Enter fullscreen mode Exit fullscreen mode

Might not be a big deal, but for me the latter takes less mental effort.

codesandbox.io/s/heuristic-wiles-p...

Thread Thread
 
iamludal profile image
Ludal 🚀

Okay now I see the benefits, and I totally agree with you! Never had to deal with such complex examples though, but it's great to have this library in mind. 🙌

Thread Thread
 
ericbdev profile image
Eric

But I can see this saving time again and again just due to its simplified mental model.

Writing nested spread after nested spread gets a little complicated after a while. Now I'm so far down the pattern that when I see syntax looking like it's mutating a variable out of scope I balk a little. All the same, it would be a nice tool.

Thread Thread
 
op profile image
op

Yeah, it definitely feels dirty writing it that way at first but it's a small price to pay for getting away from some nested spread abominations.

Collapse
 
alvisonhunter profile image
Alvison Hunter Arnuero | Front-End Web Developer

Superbe, merci beaucoup, pote!

Collapse
 
iamludal profile image
Ludal 🚀

Avec plaisir ! You're the most welcome.