1. ホーム
  2. javascript

[解決済み] Reactフック:コールバック内から最新の状態にアクセスする

2022-05-29 20:42:56

質問

編集 (2020年6月22日): この質問に対する関心が再燃しているため、いくつかの点で混乱があることに気づきました。そこで、強調したいのは、問題の例はおもちゃの例として意図されていることです。この問題を反映しているわけではありません。この質問につながった問題は、サードパーティのライブラリ(コントロールが限られています)を使用して、関数の引数としてコールバックを取るというものです。そのコールバックに最新の状態を提供する正しい方法は何でしょうか。reactクラスでは、これは this . Reactフックでは、ステートが React.useState() の関数にカプセル化されているため、もしコールバック を取得します。 を通して状態を取得します。 React.useState() を通して取得する場合、それは古い値 (コールバックがセットアップされたときの値) になります。しかし、もし が設定されると を設定すると、渡された引数を通じて最新の状態にアクセスできるようになります。つまり、このようなコールバックでReactフックを使って最新の状態を取得できる可能性があるのは、次のような方法です。 設定 を設定することで最新の状態を取得できる可能性があります。これは動作しますが、直感に反しています。

-- 元の質問は以下に続きます。

Reactフックを使用しており、コールバック内から状態を読み取ろうとしています。コールバックがそれにアクセスするたびに、デフォルトの値に戻ります。

以下のようなコードで。コンソールに表示され続ける Count is: 0 を何度クリックしても表示されません。

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  
  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${count}`)})
      setCallbackSetup(true)
    }
  }
  
  
  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);
  
}

const el = document.querySelector("#root");
ReactDOM.render(<Card title='Example Component' />, el);

次のようなコードがあります。 はこちら

コールバック内で状態を設定するのは問題ありませんが、最新の状態にアクセスすることだけが問題でした。

推測するに、状態を変更するとCard関数の新しいインスタンスが作成されるのではと思います。そして、コールバックは古いものを参照しています。以下のドキュメントに基づくと https://reactjs.org/docs/hooks-reference.html#functional-updates のドキュメントに基づき、私は、コールバック内で setState を呼び出し、setState に関数を渡して、setState 内から現在の状態にアクセスできるかどうかを確認するというアプローチを取ることを思いつきました。置き換える

setupConsoleCallback(() => {console.log(`Count is: ${count}`)})

setupConsoleCallback(() => {setCount(prevCount => {console.log(`Count is: ${prevCount}`); return prevCount})})

次のようなコードがあります。 はこちら

このアプローチもうまくいきませんでした。 編集部:実はその2つ目のアプローチ が動作します。私のコールバックにタイプミスがあっただけです。これは正しいアプローチです。以前の状態にアクセスするために、setStateを呼び出す必要があります。状態を設定するつもりがないのに。

Reactのクラスで似たようなアプローチを取った気がしますが コードの一貫性を考えると、React Effectsにこだわる必要がありますね。

コールバック内から最新の状態情報にアクセスするにはどうしたらいいですか?

どのように解決するのですか?

あなたのシナリオ (新しいコールバックを作成してサードパーティ ライブラリに渡し続けることができない場合) に対しては useRef を使って、現在の状態を持つミュータブルオブジェクトを保持することができます。 このように。

function Card(title) {
  const [count, setCount] = React.useState(0)
  const [callbackSetup, setCallbackSetup] = React.useState(false)
  const stateRef = useRef();

  // make stateRef always have the current count
  // your "fixed" callbacks can refer to this object whenever
  // they need the current value.  Note: the callbacks will not
  // be reactive - they will not re-run the instant state changes,
  // but they *will* see the current value whenever they do run
  stateRef.current = count;

  function setupConsoleCallback(callback) {
    console.log("Setting up callback")
    setInterval(callback, 3000)
  }

  function clickHandler() {
    setCount(count+1);
    if (!callbackSetup) {
      setupConsoleCallback(() => {console.log(`Count is: ${stateRef.current}`)})
      setCallbackSetup(true)
    }
  }


  return (<div>
      Active count {count} <br/>
      <button onClick={clickHandler}>Increment</button>
    </div>);

}

コールバックはミュータブルオブジェクトを参照して、現在の状態を読み取ることができます。 これはクロージャでミュータブルオブジェクトをキャプチャし、レンダリングごとにミュータブルオブジェクトは現在の状態の値で更新されます。