この投稿は別サイトからのクロス投稿です(クロス元:https://qiita.com/terrierscript/...

react-hooksのuseStateはこれまでのstateのように値を保持してくれる重要な関数だ。

例えば単純なカウンターならこんな具合になるだろう

const useCounter = () => {  
  const [count, setCounter] = useState(0)  
  return { count, setCounter   
}  
export const MyApp = () => {  
  const { count, setCounter } = useCounter()  
  return (  
    <div>  
      <div>{count}</div>  
      <button onClick={() => setCounter(count + 1)}>+</button>  
    </div>  
  )  
}

このuseStateに関数を保持させたい場合、ちょっと注意が必要になる。

例えばこんな風に書くと、意図しない挙動になるだろう。

const initialHelloFn = () => {  
  console.log("initial")  
}  
const useCounter = () => {  
  const [count, setCounter] = useState(0)  
  const [helloFn, setHelloFn] = useState(initialHelloFn)  
  // このnewFnが何度も呼び出される  
  const newFn = useCallback(() => {  
    console.log("hello!", count)  
  }, [count])  
  useEffect(() => {  
    setHelloFn(newFn)  
  }, [newFn, setHelloFn])  
  console.log(helloFn)  
  return {  
    count,  
    setCounter,  
    helloFn  
  }  
}  

export const MyApp = () => {  
  const { count, setCounter, helloFn } = useCounter()  
  return (  
    <div>  
      <div>{count}</div>  
      <button onClick={() => setCounter(count + 1)}>+</button>  
      <button onClick={() => helloFn()}>hello</button>  
    </div>  
  )  
}

正しく動かす場合は下記のようになるだろう。
{fn: 管理したい関数} のようにobjectで管理する。

const useCounter = () => {  
  const [count, setCounter] = useState(0)  
  // {fn: Function}などobjectの形にラップする  
  const [helloFn, setHelloFn] = useState({ fn: initialHelloFn })  
  const newFn = useCallback(() => {  
    console.log("hello!", count)  
  }, [count])  

  useEffect(() => {  
    setHelloFn({ fn: newFn })  
  }, [newFn, setHelloFn])  

  return {  
    count,  
    setCounter,  
    helloFn: helloFn.fn  
  }  
}

なぜこうする必要があるか?

結論から言えばuseStateから返ってくるsetFooのようなハンドラーはFunctional Updateに対応しているため、単純に関数を渡してしまうとFunctional Updateとして処理されてしまうからになる。

https://reactjs.org/docs/hooks-reference.html#functional-updates

Functional Update自体は便利で、下記のように、現在の値を引き継がずに関数だけで利用することができる

<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>

これはhooksで新しく入ったわけではなく、React.Component.setStateにも同様の機能が存在していたものだ
https://reactjs.org/docs/react-component.html#setstate

ただComponentの場合はstate自体がobjectなのでほとんどこのような引っかかり方をすることは無かった。useStateが単純な値を格納するものとして利用できてる分、この点は気をつけるべき部分だろう

関連記事

この記事へのコメント

まだコメントはありません
+1
0
@terrierの技術ブログ
このエントリーをはてなブックマークに追加