0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

useStateとFunctional updates

Last updated at Posted at 2020-07-12

問題

問題です。以下のコンポーネントでボタンをクリックすると最終的に useItems から返ってくる items はどのような値になるでしょうか?

import React, { useEffect, useState, useRef } from "react";

const useItems = () => {
  const [items, setItems] = useState([])
  const idRef = useRef(0)

  const push = () => {
    const id = idRef.current++
    setItems(items.concat({ id }))
  }

  return {
    items,
    push,
  }
}


export default function App() {
  const { items, push } = useItems()

  return (
    <div className="App">
      <button 
        type="button"
        onClick={() => { push(); push(); push() }}
      >
        PUSH PUSH PUSH!!!
      </button>

      <ul>
        {items.map((item) => <li key={item.id}>{item.id}</li>)}
      </ul>
    </div>
  )
}

メタ読みすれば [0, 1, 2] ではないですよね…

正解は [2] です!

解説

何故でしょう?
push はクロージャから items をとってきて setItems(items.concat({ id })) をしていますが items 自身が更新されるわけではありません。
なので3回呼ばれている同一の push では毎回 setItems([].concat({ id })) され、最終的には setItems([2]) されるからです。

setState は引数として (currentState) => nextState な関数を受け取ることができます(Functional updates)。
これを使って上のような問題が起きないように書き換えましょう。

import React, { useEffect, useState, useRef } from "react";

const useItems = () => {
  const [items, setItems] = useState([])
  const idRef = useRef(0)

  const push = () => {
    const id = idRef.current++
    setItems((currentItems) => currentItems.concat({ id }))
  }

  return {
    items,
    push,
  }
}


export default function App() {
  const { items, push } = useItems()

  return (
    <div className="App">
      <button 
        type="button"
        onClick={() => { push(); push(); push() }}
      >
        PUSH PUSH PUSH!!!
      </button>

      <ul>
        {items.map((item) => <li key={item.id}>{item.id}</li>)}
      </ul>
    </div>
  )
}

こうすれば useItems から返ってくる items は期待通り [0, 1, 2] になります。
なんとなく書いていると意外とあれ?と思うことが起きがちかもなと思いました。

実際に触れるCodeSandboxを置いておきます↓
https://codesandbox.io/s/blissful-robinson-329w2?file=/src/App.js

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?