「労働者が自分の仕事をうまくやりたいなら、まず自分の道具を研ぎ澄まさなければなりません。」 - 孔子、「論語。陸霊公」
表紙 > プログラミング > React: 古いクロージャ

React: 古いクロージャ

2024 年 9 月 2 日に公開
ブラウズ:414

この投稿では、useState フック React アプリでクロージャーを作成する方法を示します。

クロージャとは何かについては説明しません。このトピックに関するリソースは数多くあり、繰り返しになることは避けたいからです。 @imranabdulmalik によるこの記事を読むことをお勧めします。

要するに、クロージャは (Mozilla から):

です。

...一緒にバンドルされた (囲まれた) 関数とその周囲の状態 (語彙環境) への参照の組み合わせ。言い換えれば、クロージャを使用すると、内部関数から外部関数のスコープにアクセスできるようになります。 JavaScript では、関数が作成されるたびに、関数の作成時にクロージャが作成されます.

語彙環境という用語に慣れていない場合に備えて、@soumyadey によるこの記事、またはこの記事を読むことができます。

問題

React アプリケーションでは、useState フックで作成されたコンポーネントの状態に属する変数のクロージャーを誤って作成してしまう可能性があります。これが発生すると、古いクロージャの問題に直面することになります。つまり、状態の古い値を参照すると、その間に値が変更され、関連性が低くなります。

]

POC

Demo React アプリケーションを作成しました。その主な目的は、setTimeout メソッドのコールバック内のクロージャで閉じることができる (状態に属する) カウンターをインクリメントすることです。

つまり、このアプリは次のことができます:

  • カウンタの値を表示
  • カウンタを 1 増やす
  • 5 秒後にカウンターを 1 つ増やすタイマーを開始します。
  • カウンターを 10 増やす

次の図では、カウンターが 0 の、アプリの初期 UI 状態が示されています。

React: stale closure

カウンターの閉鎖を 3 つのステップでシミュレートします:

  1. カウンタを 1 増やす

React: stale closure

  1. 5 秒後に 1 ずつ増加するタイマーを開始します

React: stale closure

  • タイムアウトがトリガーされる前に 10 ずつ増加します React: stale closure

5 秒後、カウンターの値は 2 になります。

React: stale closure

カウンターの期待値は 12 であるはずですが、2 が得られます。

これが発生する理由は、setTimeout に渡されるコールバック内で カウンタのクロージャ を作成し、タイムアウトがトリガーされたときにそのカウンタから開始してカウンタを設定したためです。古い値 (1 でした)。

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);

アプリ コンポーネントの完全なコードに従います。

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter   1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;

    解決

    この解決策は、レンダリングに必要のない値を参照できる useRef フックの使用に基づいています。

    そこで、App コンポーネントに追加します:

    const currentCounter = useRef(counter)
    

    次に、setTimeout のコールバックを以下のように変更します。

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current   1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    

    現在の値をインクリメントする前にログに記録するため、コールバックはカウンター値を読み取る必要があります。

    値を読み取る必要がない場合は、関数表記を使用してカウンターを更新するだけでカウンターのクローズを回避できます。

    seCounter(c => c   1)
    

    リソース

    • Dmitri Pavlutin React フックを使用するときは古いクロージャに注意する
    • Imran Abdulmalik JavaScript でクロージャをマスターする: 包括的なガイド
    • JavaScript の Keyur Paralkar 語彙スコープ – 初心者ガイド
    • React での Souvik Paul の古いクロージャー
    • Soumya Dey JavaScript における語彙のスコープとクロージャの理解
    • スバシュ・マハパトラ スタックオーバーフロー
    リリースステートメント この記事は次の場所に転載されています: https://dev.to/animusna/react-stale-closure-81a?1 侵害がある場合は、[email protected] に連絡して削除してください。
    最新のチュートリアル もっと>
    • ハクトーバーフェストに貢献する新しい方法: フロントエンド AI で直接
      ハクトーバーフェストに貢献する新しい方法: フロントエンド AI で直接
      Hacktoberfest が帰ってきます。今年は開発者が参加できるエキサイティングな新しい方法をもたらします。 従来の GitHub プル リクエストの代わりに、Webcrumbs プラットフォーム上のフロントエンド AI を通じて直接テンプレートを作成して送信できるようになりました。 tools...
      プログラミング 2024 年 11 月 7 日に公開
    • 関数ポインタが括弧なしで使用されると、「cout」に「1」が出力されるのはなぜですか?
      関数ポインタが括弧なしで使用されると、「cout」に「1」が出力されるのはなぜですか?
      なぜ「関数を呼び出さずに関数を出力します (f() ではなく f;)。常に 1 を出力しますか?」この中でこのコードでは、括弧を使用せずに pr という名前の関数を「呼び出し」ようとします。ただし、これは実際には関数を呼び出しているわけではありません。代わりに、関数ポインタを cout 関数に渡しま...
      プログラミング 2024 年 11 月 7 日に公開
    • Web ページを高速化する
      Web ページを高速化する
      DOM とは何ですか?それは何を食べるのでしょうか? DOM (Document Object Model) は Web ページとその開発の基礎です。これは、HTML および XML ドキュメント用のプログラミング インターフェイスであり、ドキュメントの構造をツリー状のオブジェクト...
      プログラミング 2024 年 11 月 7 日に公開
    • JavaScript での require と import
      JavaScript での require と import
      コーディングを始めたとき、require() を使用してモジュールやインポートを使用して他のファイルをインポートするいくつかの js ファイルを見たことを覚えています。何が違うのか、なぜプロジェクト間で一貫性がないのかがよくわからず、いつも混乱していました。同じことを疑問に思っている場合は、読み続け...
      プログラミング 2024 年 11 月 7 日に公開
    • イメージを使用した Vite/React アプリケーションのデプロイ: 完全ガイド
      イメージを使用した Vite/React アプリケーションのデプロイ: 完全ガイド
      Vite/React アプリケーションを GitHub Pages にデプロイすることはエキサイティングなマイルストーンですが、このプロセスでは、特に画像やアセットを扱う場合、予期せぬ課題が発生することがあります。このブログ投稿では、初期導入から一般的な問題のトラブルシューティング、効果的な解決策の...
      プログラミング 2024 年 11 月 7 日に公開
    • React アプリで API 呼び出しを最適化した方法
      React アプリで API 呼び出しを最適化した方法
      React 開発者として、私たちは、複数の急速な状態変化を API と同期する必要があるシナリオによく直面します。小さな変更ごとに API 呼び出しを行うのは非効率的であり、クライアントとサーバーの両方に負担がかかる可能性があります。ここで、デバウンスと賢明な状態管理が機能します。この記事では、ペイ...
      プログラミング 2024 年 11 月 7 日に公開
    • さあ行こう!
      さあ行こう!
      GO を試す必要がある理由 Go は、高速かつ軽量で静的に型付けされたコンパイル言語で、効率的で信頼性の高いアプリケーションの構築に最適です。そのシンプルさとクリーンな構文により、特に初心者にとって、学習と使用が簡単になります。 Go の優れた機能には、ゴルーチンによる組み込み同時...
      プログラミング 2024 年 11 月 6 日に公開
    • PNG 画像を CSS データ URI の Base64 としてエンコードするにはどうすればよいですか?
      PNG 画像を CSS データ URI の Base64 としてエンコードするにはどうすればよいですか?
      CSS データ URI の PNG 画像に Base64 エンコーディングを使用するデータ URI を使用して PNG 画像を CSS スタイルシートに埋め込むには、PNG データ最初に Base64 形式にエンコードする必要があります。この手法を使用すると、外部画像ファイルをスタイルシート内に直接...
      プログラミング 2024 年 11 月 6 日に公開
    • API 時間別データの応答性の高い JavaScript カルーセル
      API 時間別データの応答性の高い JavaScript カルーセル
      I almost mistook an incomplete solution for a finished one and moved on to work on other parts of my weather app! While working on the carousel that w...
      プログラミング 2024 年 11 月 6 日に公開
    • Web 開発における PHP と JavaScript の主な違いは何ですか?
      Web 開発における PHP と JavaScript の主な違いは何ですか?
      PHP と JavaScript: サーバー側とクライアント側 PHP は JavaScript とは異なる役割を果たします。 PHPはサーバーサイドで動作します。サーバーはアプリケーションを実行します。フォームなどを処理します。フォームを送信すると、PHP がそれを処理します。一...
      プログラミング 2024 年 11 月 6 日に公開
    • C++ で構造体とクラスのメンバーを反復処理して、実行時に名前と値にアクセスするにはどうすればよいですか?
      C++ で構造体とクラスのメンバーを反復処理して、実行時に名前と値にアクセスするにはどうすればよいですか?
      構造体とクラスのメンバーの反復C では、構造体またはクラスのメンバーを反復して名前を取得することができます。そして価値観。これを実現するためのいくつかのアプローチを次に示します。マクロの使用REFLECTABLE マクロを使用して、イントロスペクションを可能にする構造体を定義できます。マクロは、構造...
      プログラミング 2024 年 11 月 6 日に公開
    • 項目 正確な答えが必要な場合は、float と double を避ける
      項目 正確な答えが必要な場合は、float と double を避ける
      float と double の問題: 科学的および数学的計算用に設計されており、2 進浮動小数点演算を実行します。 金銭の計算や正確な答えが必要な状況には適していません。 0.1 などの 10 の負の累乗を正確に表すことができないため、エラーが発生します。 例 1: ドル額を減算する際の計算が正し...
      プログラミング 2024 年 11 月 6 日に公開
    • Go で WebSocket を使用してリアルタイム通信を行う
      Go で WebSocket を使用してリアルタイム通信を行う
      チャット アプリケーション、ライブ通知、共同作業ツールなど、リアルタイムの更新が必要なアプリを構築するには、従来の HTTP よりも高速でインタラクティブな通信方法が必要です。そこで WebSocket が登場します。今日は、アプリケーションにリアルタイム機能を追加できるように、Go で WebSo...
      プログラミング 2024 年 11 月 6 日に公開
    • Python でプロキシを使用して Selenium Webdriver を実行する方法
      Python でプロキシを使用して Selenium Webdriver を実行する方法
      Python でプロキシを使用して Selenium Webdriver を実行するSelenium Webdriver スクリプトを Python スクリプトとしてエクスポートし、コマンド ラインから実行しようとすると、次のような問題が発生する場合があります。使用上の問題 プロキシの場合にエラーが...
      プログラミング 2024 年 11 月 6 日に公開
    • || がいつ行われるか演算子は JavaScript でデフォルトの演算子として機能しますか?
      || がいつ行われるか演算子は JavaScript でデフォルトの演算子として機能しますか?
      || の目的を理解するJavaScript の非ブール オペランドを持つ演算子JavaScript では、|| は演算子は論理 OR 演算子と呼ばれることが多く、通常はブール式を評価するために使用されます。ただし、 || が次のような場合に遭遇する可能性があります。演算子は非ブール値で使用されます...
      プログラミング 2024 年 11 月 6 日に公開

    免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。

    Copyright© 2022 湘ICP备2022001581号-3