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>
  )
}
skyboyer
  • 22,209
  • 7
  • 57
  • 64
Jithin Ks
  • 479
  • 2
  • 9
  • 18
  • Is there any reason you're trying to implement a stateful component as a functional component? – CerebralFart Feb 19 '19 at 15:55
  • 17
    @SZenC why wouldn't they, now that hooks exist? – Nicholas Tower Feb 19 '19 at 15:56
  • 2
    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` now – Isaac Feb 19 '19 at 15:58
  • 3
    `[...myArray, myArray[myArray.length-1]+1] !== myArray`, which will cause the component to re-render. – Tholle Feb 19 '19 at 16:01
  • @Tholle 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? – Jithin Ks Feb 19 '19 at 16:14
  • 1
    @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. – Tholle Feb 19 '19 at 16:18

4 Answers4

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>
  );
}
Henry Heath
  • 1,072
  • 11
  • 19
K..
  • 4,044
  • 6
  • 40
  • 85
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

Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
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>
  )
}
0

Using map works better.

    setArray(array.map(f=>{
        if(f.name===name) f.checked = checked //your code here
        return f
    }))
Fischer Tirado
  • 321
  • 3
  • 5