From f4498ef670b2112091f8aee2d929a2290b745269 Mon Sep 17 00:00:00 2001 From: Soichiro Miki Date: Tue, 9 May 2023 23:35:12 +0900 Subject: [PATCH 1/3] Translate two articles under "Adding Interactivity" - state-as-a-snapshot - queueing-a-series-of-state-updates --- .../queueing-a-series-of-state-updates.md | 144 ++++++++--------- src/content/learn/state-as-a-snapshot.md | 147 +++++++++--------- src/sidebarLearn.json | 4 +- 3 files changed, 148 insertions(+), 147 deletions(-) diff --git a/src/content/learn/queueing-a-series-of-state-updates.md b/src/content/learn/queueing-a-series-of-state-updates.md index a63b7205b..89f108251 100644 --- a/src/content/learn/queueing-a-series-of-state-updates.md +++ b/src/content/learn/queueing-a-series-of-state-updates.md @@ -1,23 +1,23 @@ --- -title: Queueing a Series of State Updates +title: 一連の state の更新をキューに入れる --- -Setting a state variable will queue another render. But sometimes you might want to perform multiple operations on the value before queueing the next render. To do this, it helps to understand how React batches state updates. +state 変数をセットすることで、新しいレンダーがキューに予約されます。しかし、次のレンダーをキューに入れる前に、state の値に対して複数の操作を行いたい場合があります。このためには、React が state の更新をどのようにバッチ処理(batching, 一括処理)するのかについて理解することが役立ちます。 -* What "batching" is and how React uses it to process multiple state updates -* How to apply several updates to the same state variable in a row +* 「バッチ処理」とは何か、React が複数の state 更新を処理する際にどのように使用されるのか +* 同じ state 変数に対し連続して複数の更新を適用する方法 -## React batches state updates {/*react-batches-state-updates*/} +## React は state 更新をまとめて処理する {/*react-batches-state-updates*/} -You might expect that clicking the "+3" button will increment the counter three times because it calls `setNumber(number + 1)` three times: +以下で "+3" ボタンをクリックした場合、`setNumber(number + 1)` を 3 回呼び出しているので、カウンタが 3 回インクリメントされると思うかもしれません。 @@ -47,7 +47,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -However, as you might recall from the previous section, [each render's state values are fixed](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time), so the value of `number` inside the first render's event handler is always `0`, no matter how many times you call `setNumber(1)`: +しかし、前のセクションで説明したように、[個々のレンダー内の state 値は固定です](/learn/state-as-a-snapshot#rendering-takes-a-snapshot-in-time)。従って `setNumber(1)` を何度呼び出しても、最初のレンダー内ではイベントハンドラ内の `number` の値は常に `0` です。 ```js setNumber(0 + 1); @@ -55,21 +55,21 @@ setNumber(0 + 1); setNumber(0 + 1); ``` -But there is one other factor at play here. **React waits until *all* code in the event handlers has run before processing your state updates.** This is why the re-render only happens *after* all these `setNumber()` calls. +しかしながら、ここにもう 1 つ別の要素が関わってきます。**イベントハンドラ内のすべてのコードが実行されるまで、React は state の更新処理を待機します**。このため、再レンダーはこれらの `setNumber()` 呼び出しがすべて終わった後で行われます。 -This might remind you of a waiter taking an order at the restaurant. A waiter doesn't run to the kitchen at the mention of your first dish! Instead, they let you finish your order, let you make changes to it, and even take orders from other people at the table. +レストランで注文を取るウェイターの話を思い出すかもしれません。ウェイターは最初の料理の注文を聞いた瞬間にキッチンにかけこむわけではありません! 代わりに、客の注文を最後まで聞き、訂正がある場合はそれも聞き取り、さらにはテーブルの他の客からの注文もまとめて受け取るはずです。 - + -This lets you update multiple state variables--even from multiple components--without triggering too many [re-renders.](/learn/render-and-commit#re-renders-when-state-updates) But this also means that the UI won't be updated until _after_ your event handler, and any code in it, completes. This behavior, also known as **batching,** makes your React app run much faster. It also avoids dealing with confusing "half-finished" renders where only some of the variables have been updated. +これにより、複数の state 変数(複数のコンポーネントからの場合も含む)の更新を、[再レンダー](/learn/render-and-commit#re-renders-when-state-updates)をあまりに頻繁にトリガすることなしに行うことができます。これは、イベントハンドラおよびその中のコードがすべて完了した*後*まで、UI は更新されないということでもあります。このような動作は**バッチ処理**(バッチング)とも呼ばれ、これにより React アプリの動作がずっと高速になります。またこれは、変数のうち一部のみが更新された「中途半端な」レンダー結果に混乱させられずに済むということでもあります。 -**React does not batch across *multiple* intentional events like clicks**--each click is handled separately. Rest assured that React only does batching when it's generally safe to do. This ensures that, for example, if the first button click disables a form, the second click would not submit it again. +**React は、クリックのような意図的に引き起こされるイベントが*複数*ある場合、それらのバッチ処理を行いません**。各クリックは別々に処理されます。React は一般的に安全と判断される場合にのみバッチ処理を行いますので、安心してください。たとえば、最初のボタンクリックでフォームを無効にしたのであれば、2 度目のクリックでフォームが再び送信されてしまわないことが保証されます。 -## Updating the same state multiple times before the next render {/*updating-the-same-state-multiple-times-before-the-next-render*/} +## 次のレンダー前に同じ state を複数回更新する {/*updating-the-same-state-multiple-times-before-the-next-render*/} -It is an uncommon use case, but if you would like to update the same state variable multiple times before the next render, instead of passing the *next state value* like `setNumber(number + 1)`, you can pass a *function* that calculates the next state based on the previous one in the queue, like `setNumber(n => n + 1)`. It is a way to tell React to "do something with the state value" instead of just replacing it. +一般的なユースケースではありませんが、次のレンダー前に同じ state 変数を複数回更新する場合、`setNumber(number + 1)` のようにして*次の state 値*を渡す代わりに、`setNumber(n => n + 1)` のようにキュー内のひとつ前の state に基づいて次の state を計算する*関数*を渡すことができます。これは、state の値を単に置き換える代わりに、React に「その state の値に対してこのようにせよ」と伝えるための手段です。 -Try incrementing the counter now: +このカウンタをインクリメントしてみてください。 @@ -99,10 +99,10 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Here, `n => n + 1` is called an **updater function.** When you pass it to a state setter: +ここで、`n => n + 1` は**更新用関数 (updater function)** と呼ばれます。これを state のセッタに渡すと: -1. React queues this function to be processed after all the other code in the event handler has run. -2. During the next render, React goes through the queue and gives you the final updated state. +1. React はこの関数をキューに入れて、イベントハンドラ内の他のコードがすべて実行された後に処理されるようにします。 +2. 次のレンダー中に、React はキューを処理し、最後に更新された state を返します。 ```js setNumber(n => n + 1); @@ -110,26 +110,26 @@ setNumber(n => n + 1); setNumber(n => n + 1); ``` -Here's how React works through these lines of code while executing the event handler: +以下に、イベントハンドラを実行するときに、React はこれらのコードをどのように処理するかを示します。 -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. -1. `setNumber(n => n + 1)`: `n => n + 1` is a function. React adds it to a queue. +1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。 +1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。 +1. `setNumber(n => n + 1)`: `n => n + 1` は関数。React はこれをキューに追加する。 -When you call `useState` during the next render, React goes through the queue. The previous `number` state was `0`, so that's what React passes to the first updater function as the `n` argument. Then React takes the return value of your previous updater function and passes it to the next updater as `n`, and so on: +次のレンダー中に `useState` が呼び出されると、React はこのキューを処理します。前回 `number` という state の値は `0` だったので、それがひとつ目の更新用関数の引数 `n` に渡されます。React はひとつ前の更新用関数の戻り値を取得し、それを次の更新用関数の `n` に渡し、というように続いていきます: -| queued update | `n` | returns | +| キュー内の更新処理 | `n` | 戻り値 | |--------------|---------|-----| | `n => n + 1` | `0` | `0 + 1 = 1` | | `n => n + 1` | `1` | `1 + 1 = 2` | | `n => n + 1` | `2` | `2 + 1 = 3` | -React stores `3` as the final result and returns it from `useState`. +React は `3` を最終結果として保存し、`useState` から返します。 -This is why clicking "+3" in the above example correctly increments the value by 3. -### What happens if you update state after replacing it {/*what-happens-if-you-update-state-after-replacing-it*/} +以上が、上記の例で "+3" をクリックすると、値が正しく 3 ずつ増加する理由です。 +### state を置き換えた後に更新するとどうなるか {/*what-happens-if-you-update-state-after-replacing-it*/} -What about this event handler? What do you think `number` will be in the next render? +では、このイベントハンドラはどうでしょうか? 次回のレンダーで `number` の値はどうなっていると思いますか? ```js ``` -Here is what this button's click handler tells React to do: +このボタンのクリックハンドラは、以下のように React に指示しています。 -1. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. -2. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. -3. `setNumber(number + 1)`: `number` is `0` so `setNumber(0 + 1)`. - - React prepares to change `number` to `1` on the next render. +1. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。 + - React は次回のレンダーで `number` を `1` に更新する準備をする。 +2. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。 + - React は次回のレンダーで `number` を `1` に更新する準備をする。 +3. `setNumber(number + 1)`: `number` が `0` なので `setNumber(0 + 1)`。 + - React は次回のレンダーで `number` を `1` に更新する準備をする。 -Even though you called `setNumber(number + 1)` three times, in *this render's* event handler `number` is always `0`, so you set the state to `1` three times. This is why, after your event handler finishes, React re-renders the component with `number` equal to `1` rather than `3`. +`setNumber(number + 1)` を 3 回呼び出しましたが、*今回のレンダーの*イベントハンドラでは `number` は常に `0` なので、state を 3 回連続して `1` にセットしていることになります。これが、イベントハンドラが終了した後、React が `number` を `3` ではなく `1` とした上でコンポーネントを再レンダーする理由です。 -You can also visualize this by mentally substituting state variables with their values in your code. Since the `number` state variable is `0` for *this render*, its event handler looks like this: +もっと分かりやすくするために、頭の中でコード内の state 変数を実際の値に置換してみることもできます。*このレンダーでは* `number` という state 変数は `0` なので、イベントハンドラは次のようになっています。 ```js ``` -For the next render, `number` is `1`, so *that render's* click handler looks like this: +次のレンダーでは、`number` が `1` になるため、*そちらのレンダーの* クリックハンドラは、次のようになります。 ```js ``` -This is why clicking the button again will set the counter to `2`, then to `3` on the next click, and so on. +以上が、ボタンを再度クリックするとカウンタが `2` にセットされ、次のクリックでは `3` にセットされ、というようになる理由です。 -## State over time {/*state-over-time*/} +## 時間経過と state {/*state-over-time*/} -Well, that was fun. Try to guess what clicking this button will alert: +なかなか面白い話でした。それでは、このボタンをクリックするとアラートに何が表示されるか予想してみてください。 @@ -203,14 +203,14 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -If you use the substitution method from before, you can guess that the alert shows "0": +上記で説明した置換メソッドを使えば、アラートには "0" と表示されることがわかりますね。 ```js setNumber(0 + 5); alert(0); ``` -But what if you put a timer on the alert, so it only fires _after_ the component re-rendered? Would it say "0" or "5"? Have a guess! +でも、アラートにタイマーを設定して、コンポーネントが再レンダーされた*後に*発火するようにしたらどうなるでしょうか? "0" と表示されるのか、"5" と表示されるのか推測してみてください。 @@ -241,7 +241,7 @@ h1 { display: inline-block; margin: 10px; width: 30px; text-align: center; } -Surprised? If you use the substitution method, you can see the "snapshot" of the state passed to the alert. +驚いたでしょうか? さきほどの置換メソッドを使ってみれば、アラートに渡された state が「スナップショット」であることが分かるでしょう。 ```js setNumber(0 + 5); @@ -250,16 +250,16 @@ setTimeout(() => { }, 3000); ``` -The state stored in React may have changed by the time the alert runs, but it was scheduled using a snapshot of the state at the time the user interacted with it! +アラートが実行される時点では React に格納されている state は既に更新されているかもしれませんが、アラートはユーザがボタンを操作した時点での state のスナップショットを使ってスケジューリングされました! -**A state variable's value never changes within a render,** even if its event handler's code is asynchronous. Inside *that render's* `onClick`, the value of `number` continues to be `0` even after `setNumber(number + 5)` was called. Its value was "fixed" when React "took the snapshot" of the UI by calling your component. +イベントハンドラのコードが非同期であっても、**レンダー内の state 変数の値は決して変わりません**。*そのレンダーの* `onClick`内では、`setNumber(number + 5)` が呼ばれた後も `number` の値は `0` のままです。その値は React があなたのコンポーネントを呼び出して UI の「スナップショットを取った」時に、「固定」されたのです。 -Here is an example of how that makes your event handlers less prone to timing mistakes. Below is a form that sends a message with a five-second delay. Imagine this scenario: +ここで、このお陰でタイミングにまつわる問題が起きづらくなっている、という例をお示しします。以下のフォームは、5 秒の遅延後にメッセージを送信します。ここでこんなシナリオを想像してみてください: -1. You press the "Send" button, sending "Hello" to Alice. -2. Before the five-second delay ends, you change the value of the "To" field to "Bob". +1. "Send" ボタンを押して、"Hello" というメッセージをアリスに送る。 +2. 5 秒の遅延が終わる前に、"To" フィールドの値を "Bob" に変更する。 -What do you expect the `alert` to display? Would it display, "You said Hello to Alice"? Or would it display, "You said Hello to Bob"? Make a guess based on what you know, and then try it: +`alert` に何が表示されると思いますか? "You said Hello to Alice" と表示されるのでしょうか? それとも "You said Hello to Bob" でしょうか? ここまでの知識に基づいて推測し、実際に試してみましょう。 @@ -305,19 +305,19 @@ label, textarea { margin-bottom: 10px; display: block; } -**React keeps the state values "fixed" within one render's event handlers.** You don't need to worry whether the state has changed while the code is running. +**React は、レンダー内の state の値を「固定」し、イベントハンドラ内で保持します**。コードが実行されている途中で state が変更されたかどうか心配する必要はありません。 -But what if you wanted to read the latest state before a re-render? You'll want to use a [state updater function](/learn/queueing-a-series-of-state-updates), covered on the next page! +しかし、再レンダー前に最新の state を読み取りたい場合はどうでしょうか? [state 更新用関数](/learn/queueing-a-series-of-state-updates)を使うことができます。これについては次のページで説明します! -* Setting state requests a new render. -* React stores state outside of your component, as if on a shelf. -* When you call `useState`, React gives you a snapshot of the state *for that render*. -* Variables and event handlers don't "survive" re-renders. Every render has its own event handlers. -* Every render (and functions inside it) will always "see" the snapshot of the state that React gave to *that* render. -* You can mentally substitute state in event handlers, similarly to how you think about the rendered JSX. -* Event handlers created in the past have the state values from the render in which they were created. +- state のセットは新しいレンダーをリクエストする。 +- React は state をコンポーネントの外側で、まるで棚に保管しておくかのようにして保持する。 +- `useState` を呼び出すと、React は*そのレンダーのための* state のスナップショットを返す。 +- 変数やイベントハンドラは複数レンダーをまたいで「生き残る」ことはない。すべてのレンダーは固有のイベントハンドラを持つ。 +- 各レンダー(およびその中の関数)からは、常に、React が *その*レンダーに渡した state のスナップショットが「見える」。 +- レンダーされた JSX を考える時と同様にして、イベントハンドラ内の state を頭の中で実際の値に置換してみることができる。 +- 過去に作成されたイベントハンドラは、それが作成されたレンダーにおける state の値を持っている。 @@ -325,9 +325,10 @@ But what if you wanted to read the latest state before a re-render? You'll want -#### Implement a traffic light {/*implement-a-traffic-light*/} +#### 信号機を実装 {/*implement-a-traffic-light*/} -Here is a crosswalk light component that toggles when the button is pressed: + +以下は、ボタンが押されると切り替わる歩行者用信号機のコンポーネントです。 @@ -362,13 +363,13 @@ h1 { margin-top: 20px; } -Add an `alert` to the click handler. When the light is green and says "Walk", clicking the button should say "Stop is next". When the light is red and says "Stop", clicking the button should say "Walk is next". +クリックハンドラに `alert` を追加してください。信号が緑で "Walk" と表示されている場合、ボタンをクリックすると "Stop is next" と表示され、信号が赤で "Stop" と表示されている場合、ボタンをクリックすると "Walk is next" と表示されるようにしてください。 -Does it make a difference whether you put the `alert` before or after the `setWalk` call? +`alert` を `setWalk` の前に置いた場合と後に置いた場合で、違いはありますか? -Your `alert` should look like this: +`alert` は以下のように書けます。 @@ -404,17 +405,17 @@ h1 { margin-top: 20px; } -Whether you put it before or after the `setWalk` call makes no difference. That render's value of `walk` is fixed. Calling `setWalk` will only change it for the *next* render, but will not affect the event handler from the previous render. +`alert` を `setWalk` の前に置いた場合と後に置いた場合で、違いはありません。このレンダー中、`walk` の値は固定です。`setWalk` を呼び出しても、*次の*レンダーまで実際の変更は起きず、現在レンダーのイベントハンドラには影響しません。 -This line might seem counter-intuitive at first: +この行は最初は直感に反しているように思えるかもしれません。 ```js alert(walk ? 'Stop is next' : 'Walk is next'); ``` -But it makes sense if you read it as: "If the traffic light shows 'Walk now', the message should say 'Stop is next.'" The `walk` variable inside your event handler matches that render's value of `walk` and does not change. +しかし、これを「もし信号が今 "Walk" を表示しているなら、メッセージは "Stop is next" と言うべきだ」のように読めば、理に適っていることが分かります。イベントハンドラ内の `walk` 変数は、そのレンダーの `walk` の値と一致しており、変わることはありません。 -You can verify that this is correct by applying the substitution method. When `walk` is `true`, you get: +上記で説明している置換メソッドを適用して、このことが正しいことを確認することができます。`walk` が `true` の場合、次のようになります。 ```js