Updating an array in React using Hooks
Asked Answered
E

4

19

I'm trying to figure out the working of React Hook API. I'm trying to add a number to a list. The code that I commented, i.e myArray.push... doesn't seem to perform the operation, though the code below it is working fine. Why is it so?

import React, {useState} from 'react'

export default () => {

  const [myArray, setArray] = useState([1,2,3])

  return (
    <div>
      {myArray.map((item=>{

        return <li>{item}</li>

      }))}
      <button onClick = {()=>{

        // myArray.push(myArray[myArray.length-1]+1)
        // setArray(myArray)

        setArray([...myArray, myArray[myArray.length-1]+1])

      }}>Add</button>
    </div>
  )
}
Elecampane answered 19/2, 2019 at 15:53 Comment(6)
Is there any reason you're trying to implement a stateful component as a functional component?Wafd
@SZenC why wouldn't they, now that hooks exist?Stoddart
I believe myArray is this.state.myArray and as usual, you shouldn't mutate the state so previously you never this.state.myArray.push(....), which also means you shouldn't try to myArray.push nowDactylic
[...myArray, myArray[myArray.length-1]+1] !== myArray, which will cause the component to re-render.Inna
@Inna That makes sense, so React compares the existing myArray and what we pass and if there is a change rerenders? And in the first case, I'm passing myArray itself to setArray, so there is no rerendering?Elecampane
@JithinKs That's right. React will do a strict === comparison under the hood, and since myArray.push just adds another element to the existing array, it will not re-render. [...myArray, myArray[myArray.length-1]+1] however creates an entirely new array. If you stick to the rule of thumb to not mutate the state directly, you will not run into these issues.Inna
I
24

I would recommend using useReducer for anything more complicated than a single value.

function App() {
  const [input, setInput] = useState(0);

  const [myArray, dispatch] = useReducer((myArray, { type, value }) => {
    switch (type) {
      case "add":
        return [...myArray, value];
      case "remove":
        return myArray.filter((_, index) => index !== value);
      default:
        return myArray;
    }
  }, [1, 2, 3]);

  return (
    <div>
      <input value={input} onInput={e => setInput(e.target.value)} />
      <button onClick={() => dispatch({ type: "add", value: input})}>
        Add
      </button>

      {myArray.map((item, index) => (
        <div>
          <h2>
            {item}
            <button onClick={() => dispatch({ type: "remove", value: index })}>
              Remove
            </button>
          </h2>
        </div>
      ))}
    </div>
  );
}
Idealistic answered 19/2, 2019 at 16:35 Comment(0)
O
8

You aren't mutating the array in the commenting code and hence when you try to setState, hooks internally check that the same state is being passed due to reference not updating for myArray and hence, won't trigger a re-render again.

However in the working code, you are creating a new array instance and hence the update is working correctly

Othella answered 19/2, 2019 at 16:19 Comment(1)
So you have to clone an array to update an array...? That seems necessarily expensive no?Excepting
A
2

You can use a way to deep clone an object and use it as a temporary variable to change the value of your array.

Here an example with your code :

import React, { useState } from 'react'

export default () => {
  const [myArray, setArray] = useState([1, 2, 3])
  var tmpArray = JSON.parse(JSON.stringify(myArray))

  return (
    <div>
      {myArray.map((item) => {
        return <li>{item}</li>
      })}
      <button
        onClick={() => {
          tmpArray.push(tmpArray[tmpArray.length - 1] + 1)
          setArray(tmpArray)
        }}
      >
        Add
      </button>
    </div>
  )
}
Acanthopterygian answered 26/11, 2020 at 17:34 Comment(0)
A
0

Using map works better.

    setArray(array.map(f=>{
        if(f.name===name) f.checked = checked //your code here
        return f
    }))
Allophone answered 16/1, 2023 at 4:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.