ユーザ状態の関数更新によるユーザコールバック状態依存性の解消


useStateを更新する従来の方法

import React, { useState, useCallback } from 'react';
const myCompo = () => {
  const [todos, setTodos] 
  = useState([{id:1, text:'병원가기', checked:false}]);

  const onAdd = useCallback((e)=>{
    e.preventDafault();
    setTodos(todos.concat({id:2, text:'애플스토어가기', checked:false}));
  }, [todos])

  const onRemove = useCallback((e, id)=> {
    e.preventDefault();
    let tempData = todos.filter(todo=>todo.id!==id);
    setTodos(tempData);
  }, [todos])


  return(
    // 생략..
  )
}
アクティブハンドルはonAddonRemoveです.onAddにおいて、concat関数が返す新しい配列は、setTodos()に依然として使用される.onRemoveは、filter関数によって返される新しい配列を一時変数tempDataに格納し、tempDatasetTodos()に使用される.
両方のイベントハンドルはtodosに依存します.既存のtodosをクエリーして新しい配列を作成するためです.
したがって、useCallbackは第2の因子として列挙されるべきである.なぜなら、todosが更新または変更された場合、イベントハンドラは再宣言する必要があるからです.

useState関数式更新

import React, { useState, useCallback } from 'react';
const myCompo = () => {
  const [todos, setTodos] 
  = useState([{id:1, text:'병원가기', checked:false}]);

  const onAdd = useCallback((e)=>{
    e.preventDafault();
    setTodos(todos=>
             todos.concat({id:2, text:'애플스토어가기', checked:false}));
  }, [])

  const onRemove = useCallback((e, id)=> {
    e.preventDefault();
    setTodos(todos=>todos.filter(todo=>todo.id!==id));
  }, [])


  return(
    // 생략..
  )
}
todosを関数型更新に変更します.useStateの更新方式を関数型に変更し、以下に示すようにArrow Functionの形式に変更します.
  • setTodos in setTodos
  • setTodos(todos=>todos.concat({id:2, text:'애플스토어가기', checked:false}));
  • onAdd() in setTodos
  • setTodos(todos=>todos.filter(todo=>todo.id!==id));
    これにより、onRemove()でstate値の依存性を解消し、2番目のパラメータを空の配列に指定することができる.

    なぜ依存性がないのでしょうか。


    関数型更新方式の変更に伴い、関数のパラメータはuseCallback()を超えた.
    既存の変数をパラメータに変換すると、依存性が失われます.
    let num = 10;
    function addfunc(value) {
      value = value + 10;
    }
    addfunc(num);
    console.log(num) // 10
    上記コードに示すように、todos以外で宣言された기존 변수 !== 매개변수という変数を関数のパラメータとして渡し、10を加算するロジックを行うと、実際の関数以外のaddfunc()という変数が出力され、10が出力される.関数のnumnumは異なります.
    したがって、numを関数的に更新して使用すると、함수 밖의 numからuseStateの値を直接問い合わせるのではなく、間接的に問い合わせるので依存性がなくなります.

    パフォーマンスの向上


    USStateを関数として更新しても、パフォーマンスは良くなりません.ただし、以下のコードを参照してください.
    // tempCompo.jsx
    import React, { useState, useCallback } from 'react';
    import listItem from './listItem';
    
    const tempCompo = () => {
      const [todos, setTodos] = useState(
        {
          id: 1,
          text : '학교가기',
          checked: false
        },
        {
          id: 2,
          text: '애플스토어가기',
          checked: false
        }
      )
      
      // 함수형 업데이트 방식 X
      const onToggle = useCallback(()=> {
         setTodos(todos =>
                  todos.map(todo=>
                  todo.id ===id ? {...todo, checked: !todo.checked} : todo))
      }, [todos])
      
      
      return(
        {todos.map(
         (<listItem key={todo.id} checked={todo.checked}
            text={todo.text} onToggle={onToggle}/>))}
      )
    }
    //listItem.jsx
    import React from 'react';
    const listItem = ({checked, text, onToggle}) => {
      return(
        <div> {text} </div>
        <button onClick={onToggle}>버튼</button>
      )
    }
    export default React.memo(listItem);
    以上のように、usStateは、関数更新ではなく従来の方法で実現され、useCallbackstateを用いて素子最適化が行われた.listItem.jsxの構成部品のみを表示します.React.memo()の構成部品は、ボタンを押してlistItem"학교가기"をtrueに変更しても再レンダリングされません.しかしcheckedの観点から"애플스토어가기"の値stateが変化したため、再生stateには資源浪費があった.
    USState関数式の更新によって実現される場合、素子の最適化は予想通りに行うことができる.