From ea70fb5d1cca740e0ba70579b88c60f0182e674d Mon Sep 17 00:00:00 2001 From: Soichiro Miki Date: Thu, 12 Oct 2023 17:49:01 +0900 Subject: [PATCH] Translate "memo" and unify expressions in Deep Dive --- .../learn/you-might-not-need-an-effect.md | 8 +- src/content/reference/react/memo.md | 96 +++++++++---------- src/content/reference/react/useCallback.md | 14 +-- src/content/reference/react/useMemo.md | 32 +++---- 4 files changed, 75 insertions(+), 75 deletions(-) diff --git a/src/content/learn/you-might-not-need-an-effect.md b/src/content/learn/you-might-not-need-an-effect.md index 5aad391f5..7be5b650b 100644 --- a/src/content/learn/you-might-not-need-an-effect.md +++ b/src/content/learn/you-might-not-need-an-effect.md @@ -127,9 +127,9 @@ function TodoList({ todos, filter }) { -#### 計算が高価かどうかを判断する方法 {/*how-to-tell-if-a-calculation-is-expensive*/} +#### 計算コストが高いかどうかを見分ける方法 {/*how-to-tell-if-a-calculation-is-expensive*/} -一般的に、何千ものオブジェクトを作成したりループしたりしていない限り、おそらく高価ではありません。より確信を持ちたい場合は、コードが消費した時間を測定するためにコンソールログを追加できます。 +一般的に、何千ものオブジェクトを作成したりループしたりしていない限り、おそらく高価ではありません。より確信を持ちたい場合は、コンソールログを追加して、コードの実行にかかった時間を計測することができます。 ```js {1,3} console.time('filter array'); @@ -149,9 +149,9 @@ console.timeEnd('filter array'); `useMemo` は*初回*レンダーを高速化しません。更新時に不要な作業をスキップするときにのみ役立ちます。 -あなたのマシンはおそらくユーザのものよりも高速であるということを念頭に置き、人工的に遅くした環境でパフォーマンスをテストすることも良い考えです。例えば、Chrome ではこれに対応する [CPU スロットリング](https://developer.chrome.com/blog/new-in-devtools-61/#throttling)オプションが提供されています。 +また、ほとんどの場合に、あなたが使っているマシンは、ユーザのマシンより高速に動作するであろうことを忘れてはいけません。そのため、意図的に処理速度を低下させてパフォーマンスをテストするのが良いでしょう。例えば、Chrome では [CPU スロットリング](https://developer.chrome.com/blog/new-in-devtools-61/#throttling)オプションが提供されています。 -また、開発環境でのパフォーマンス測定では完全に正確な結果は得られないことに注意してください。(例えば、[Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントが 1 度ではなく 2 度レンダーされることがあります。)最も正確に時間を計測するためには、アプリを本番環境用にビルドし、ユーザが持っているデバイスでテストしてください。 +また、開発環境でのパフォーマンス測定では完全に正確な結果は得られないことに注意してください。(例えば、[Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントが 1 度ではなく 2 度レンダーされることがあります。)最も正確にパフォーマンスを計測するためには、アプリを本番環境用にビルドし、ユーザが持っているようなデバイスでテストしてください。 diff --git a/src/content/reference/react/memo.md b/src/content/reference/react/memo.md index 26fa9ed9c..a51c6e4ff 100644 --- a/src/content/reference/react/memo.md +++ b/src/content/reference/react/memo.md @@ -4,7 +4,7 @@ title: memo -`memo` lets you skip re-rendering a component when its props are unchanged. +`memo` を使うことで、props が変更されていない場合にコンポーネントの再レンダーをスキップできます。 ``` const MemoizedComponent = memo(SomeComponent, arePropsEqual?) @@ -16,11 +16,11 @@ const MemoizedComponent = memo(SomeComponent, arePropsEqual?) --- -## Reference {/*reference*/} +## リファレンス {/*reference*/} ### `memo(Component, arePropsEqual?)` {/*memo*/} -Wrap a component in `memo` to get a *memoized* version of that component. This memoized version of your component will usually not be re-rendered when its parent component is re-rendered as long as its props have not changed. But React may still re-render it: memoization is a performance optimization, not a guarantee. +コンポーネントを `memo` でラップすることで、そのコンポーネントの*メモ化 (memoize)* されたバージョンが得られます。このメモ化されたバージョンのコンポーネントは、親コンポーネントが再レンダーされても、自身の props が変更されていない限り通常は再レンダーされなくなります。ただしメモ化は、パフォーマンス最適化であって保証ではないため、React が再レンダーを行うこともありえます。 ```js import { memo } from 'react'; @@ -30,27 +30,27 @@ const SomeComponent = memo(function SomeComponent(props) { }); ``` -[See more examples below.](#usage) +[さらに例を見る](#usage) -#### Parameters {/*parameters*/} +#### 引数 {/*parameters*/} -* `Component`: The component that you want to memoize. The `memo` does not modify this component, but returns a new, memoized component instead. Any valid React component, including functions and [`forwardRef`](/reference/react/forwardRef) components, is accepted. +* `Component`: メモ化したいコンポーネント。`memo` はこのコンポーネントを変更するのではなく、メモ化が有効になった新たなコンポーネントを返します。関数コンポーネントや [`forwardRef`](/reference/react/forwardRef) によるコンポーネントを含む、任意の有効な React コンポーネントを受け付けます。 -* **optional** `arePropsEqual`: A function that accepts two arguments: the component's previous props, and its new props. It should return `true` if the old and new props are equal: that is, if the component will render the same output and behave in the same way with the new props as with the old. Otherwise it should return `false`. Usually, you will not specify this function. By default, React will compare each prop with [`Object.is`.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +* **省略可能** `arePropsEqual`: コンポーネントの前回の props と新しい props の 2 つを引数に取る関数。古い props と新しい props が等しい場合、つまり、新しい props でもコンポーネントが古い props と同じ出力をレンダーして同じように動作する場合は `true` を返すようにします。それ以外の場合は `false` を返すようにします。通常はこの関数を指定することはありません。デフォルトでは、React は個々の props を [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使って比較します。 -#### Returns {/*returns*/} +#### 返り値 {/*returns*/} -`memo` returns a new React component. It behaves the same as the component provided to `memo` except that React will not always re-render it when its parent is being re-rendered unless its props have changed. +`memo` は新しい React コンポーネントを返します。これは `memo` に渡したコンポーネントと同様に動作しますが、親が再レンダーされた際に自身の props が変更されていない場合、React が再レンダーを行わない、という点が異なります。 --- -## Usage {/*usage*/} +## 使用法 {/*usage*/} -### Skipping re-rendering when props are unchanged {/*skipping-re-rendering-when-props-are-unchanged*/} +### props が変更されていない場合に再レンダーをスキップする {/*skipping-re-rendering-when-props-are-unchanged*/} -React normally re-renders a component whenever its parent re-renders. With `memo`, you can create a component that React will not re-render when its parent re-renders so long as its new props are the same as the old props. Such a component is said to be *memoized*. +React は通常、親コンポーネントが再レンダーされると常にコンポーネントを再レンダーします。`memo` を使用すると、新しい props が古い props と同じである限り、親が再レンダーされても React によって再レンダーされないコンポーネントを作成できます。そのようなコンポーネントは*メモ化された (memoized)* コンポーネントと呼ばれます。 -To memoize a component, wrap it in `memo` and use the value that it returns in place of your original component: +コンポーネントをメモ化するには、それを `memo` でラップし、返された値を元のコンポーネントの代わりに使用します。 ```js const Greeting = memo(function Greeting({ name }) { @@ -60,9 +60,9 @@ const Greeting = memo(function Greeting({ name }) { export default Greeting; ``` -A React component should always have [pure rendering logic.](/learn/keeping-components-pure) This means that it must return the same output if its props, state, and context haven't changed. By using `memo`, you are telling React that your component complies with this requirement, so React doesn't need to re-render as long as its props haven't changed. Even with `memo`, your component will re-render if its own state changes or if a context that it's using changes. +React コンポーネントは常に[純粋なレンダーロジック](/learn/keeping-components-pure)を持つべきです。これは、props、state およびコンテクストが変更されていない場合、常に同じ出力を返す必要があるという意味です。`memo` を使用することで、コンポーネントがこの要件を満たしており、props が変更されない限り再レンダーの必要がないということを React に伝えることができます。`memo` を使用しても、コンポーネント自体の state が変更された場合や、使用しているコンテクストが変更された場合には再レンダーが発生します。 -In this example, notice that the `Greeting` component re-renders whenever `name` is changed (because that's one of its props), but not when `address` is changed (because it's not passed to `Greeting` as a prop): +以下の例において、`name` は(props の一部なので)変更されるたびに `Greeting` コンポーネントが再レンダーされる一方で、`address` は(`Greeting` に props として渡されていないため)変更されても再レンダーされないということに注目してください。 @@ -104,37 +104,37 @@ label { -**You should only rely on `memo` as a performance optimization.** If your code doesn't work without it, find the underlying problem and fix it first. Then you may add `memo` to improve performance. +**`memo` はパフォーマンスの最適化としてのみ使用してください**。もしコードがそれなしでは動作しない場合、根本的な問題を見つけて先に修正してください。その後で `memo` を追加してパフォーマンスを向上させることができます。 -#### Should you add memo everywhere? {/*should-you-add-memo-everywhere*/} +#### あらゆる場所に memo を追加すべきか? {/*should-you-add-memo-everywhere*/} -If your app is like this site, and most interactions are coarse (like replacing a page or an entire section), memoization is usually unnecessary. On the other hand, if your app is more like a drawing editor, and most interactions are granular (like moving shapes), then you might find memoization very helpful. +あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。 -Optimizing with `memo` is only valuable when your component re-renders often with the same exact props, and its re-rendering logic is expensive. If there is no perceptible lag when your component re-renders, `memo` is unnecessary. Keep in mind that `memo` is completely useless if the props passed to your component are *always different,* such as if you pass an object or a plain function defined during rendering. This is why you will often need [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) and [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) together with `memo`. +`memo` による最適化は、コンポーネントが全く同一の props で頻繁に再レンダーされ、しかもその再レンダーロジックが高コストである場合にのみ価値があります。コンポーネントが再レンダーされても遅延を感じられない場合、`memo` は不要です。レンダー中に定義されたオブジェクトやプレーンな関数を渡しているなどでコンポーネントに渡される props が*毎回異なる*場合、`memo` は全く無意味であることを覚えておいてください。これが、`memo` と一緒に [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) や [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) がよく必要となる理由です。 -There is no benefit to wrapping a component in `memo` in other cases. There is no significant harm to doing that either, so some teams choose to not think about individual cases, and memoize as much as possible. The downside of this approach is that code becomes less readable. Also, not all memoization is effective: a single value that's "always new" is enough to break memoization for an entire component. +その他のケースでコンポーネントを `memo` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限り全てをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。 -**In practice, you can make a lot of memoization unnecessary by following a few principles:** +**実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。 -1. When a component visually wraps other components, let it [accept JSX as children.](/learn/passing-props-to-a-component#passing-jsx-as-children) This way, when the wrapper component updates its own state, React knows that its children don't need to re-render. -1. Prefer local state and don't [lift state up](/learn/sharing-state-between-components) any further than necessary. For example, don't keep transient state like forms and whether an item is hovered at the top of your tree or in a global state library. -1. Keep your [rendering logic pure.](/learn/keeping-components-pure) If re-rendering a component causes a problem or produces some noticeable visual artifact, it's a bug in your component! Fix the bug instead of adding memoization. -1. Avoid [unnecessary Effects that update state.](/learn/you-might-not-need-an-effect) Most performance problems in React apps are caused by chains of updates originating from Effects that cause your components to render over and over. -1. Try to [remove unnecessary dependencies from your Effects.](/learn/removing-effect-dependencies) For example, instead of memoization, it's often simpler to move some object or a function inside an Effect or outside the component. +1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。 +1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。 +1. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです! メモ化を追加するのではなく、バグを修正します。 +1. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。 +1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。 -If a specific interaction still feels laggy, [use the React Developer Tools profiler](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html) to see which components would benefit the most from memoization, and add memoization where needed. These principles make your components easier to debug and understand, so it's good to follow them in any case. In the long term, we're researching [doing granular memoization automatically](https://www.youtube.com/watch?v=lGEMwh32soc) to solve this once and for all. +それでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。 --- -### Updating a memoized component using state {/*updating-a-memoized-component-using-state*/} +### state を使ってメモ化されたコンポーネントを更新する {/*updating-a-memoized-component-using-state*/} -Even when a component is memoized, it will still re-render when its own state changes. Memoization only has to do with props that are passed to the component from its parent. +コンポーネントがメモ化されていても、自身の state が変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。 @@ -203,13 +203,13 @@ label { -If you set a state variable to its current value, React will skip re-rendering your component even without `memo`. You may still see your component function being called an extra time, but the result will be discarded. +state 変数を現在値そのものにセットする場合、React は `memo` がなくてもコンポーネントの再レンダーをスキップします。コンポーネント関数が余分に呼び出されることがあるかもしれませんが、その結果は破棄されます。 --- -### Updating a memoized component using a context {/*updating-a-memoized-component-using-a-context*/} +### コンテクストを使ってメモ化されたコンポーネントを更新する {/*updating-a-memoized-component-using-a-context*/} -Even when a component is memoized, it will still re-render when a context that it's using changes. Memoization only has to do with props that are passed to the component from its parent. +コンポーネントがメモ化されていても、使用しているコンテクストが変更されたときには再レンダーが発生します。メモ化は、親からコンポーネントに渡される props にのみ関係します。 @@ -263,16 +263,16 @@ label { -To make your component re-render only when a _part_ of some context changes, split your component in two. Read what you need from the context in the outer component, and pass it down to a memoized child as a prop. +コンテクストの一部が変化したときだけコンポーネントが再レンダーされるようにするには、コンポーネントを 2 つに分割してください。外側のコンポーネントでコンテクストから必要な情報を読み取って、それをメモ化された子コンポーネントに props として渡します。 --- -### Minimizing props changes {/*minimizing-props-changes*/} +### props の変更を可能な限り減らす {/*minimizing-props-changes*/} -When you use `memo`, your component re-renders whenever any prop is not *shallowly equal* to what it was previously. This means that React compares every prop in your component with its previous value using the [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) comparison. Note that `Object.is(3, 3)` is `true`, but `Object.is({}, {})` is `false`. +`memo` を使用すると、コンポーネントは props のいずれかが*浅い (shallow) 比較で*前回と等しくない場合に再レンダーされます。つまり、React はコンポーネントのすべての props を前回の値と [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) を使用して比較します。`Object.is(3, 3)` は `true` ですが、`Object.is({}, {})` は `false` です。 -To get the most out of `memo`, minimize the times that the props change. For example, if the prop is an object, prevent the parent component from re-creating that object every time by using [`useMemo`:](/reference/react/useMemo) +`memo` の利点を最大限活かすためには、props が変更される回数を最小限に抑えます。例えば、ある props がオブジェクトである場合、親コンポーネント側で、そのオブジェクトが毎回再作成されるのを防ぐために [`useMemo`](/reference/react/useMemo) を使用します。 ```js {5-8} function Page() { @@ -292,7 +292,7 @@ const Profile = memo(function Profile({ person }) { }); ``` -A better way to minimize props changes is to make sure the component accepts the minimum necessary information in its props. For example, it could accept individual values instead of a whole object: +props の変更を最小限に抑えるより良い方法は、コンポーネントが最小限の情報を props として受け入れるようにすることです。例えば、オブジェクト全体の代わりにその中の個々の値を受け入れるようにできます。 ```js {4,7} function Page() { @@ -306,7 +306,7 @@ const Profile = memo(function Profile({ name, age }) { }); ``` -Even individual values can sometimes be projected to ones that change less frequently. For example, here a component accepts a boolean indicating the presence of a value rather than the value itself: +さらにそのような個々の値を、より変化しづらい値に投射できることもあります。例えば以下では、コンポーネントが値そのものではなく、値が存在するかどうかのみを表すブーリアンを受け入れるようになっています。 ```js {3} function GroupsLanding({ person }) { @@ -319,13 +319,13 @@ const CallToAction = memo(function CallToAction({ hasGroups }) { }); ``` -When you need to pass a function to memoized component, either declare it outside your component so that it never changes, or [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) to cache its definition between re-renders. +メモ化されたコンポーネントに関数を渡す必要がある場合、それが変化しないようにコンポーネント外で宣言するか、または [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) を使用することで再レンダーをまたいで定義をキャッシュします。 --- -### Specifying a custom comparison function {/*specifying-a-custom-comparison-function*/} +### カスタム比較関数の指定 {/*specifying-a-custom-comparison-function*/} -In rare cases it may be infeasible to minimize the props changes of a memoized component. In that case, you can provide a custom comparison function, which React will use to compare the old and new props instead of using shallow equality. This function is passed as a second argument to `memo`. It should return `true` only if the new props would result in the same output as the old props; otherwise it should return `false`. +まれに、メモ化されたコンポーネントの props の変更を最小限に抑えることが不可能な場合があります。その場合、カスタム比較関数を提供することができます。React は浅い比較の代わりに、これを使用して古い props と新しい props を比較します。この関数は `memo` の第 2 引数として渡します。新しい props が古い props と同じ出力をもたらす場合にのみ `true` を返し、それ以外の場合は `false` を返すようにします。 ```js {3} const Chart = memo(function Chart({ dataPoints }) { @@ -343,21 +343,21 @@ function arePropsEqual(oldProps, newProps) { } ``` -If you do this, use the Performance panel in your browser developer tools to make sure that your comparison function is actually faster than re-rendering the component. You might be surprised. +これを行う際は、ブラウザの開発者ツールの Performance パネルを使用して、比較関数を用いることでコンポーネントを再レンダーするより実際に高速化されることを確認してください。結果に驚くかもしれません。 -When you do performance measurements, make sure that React is running in the production mode. +パフォーマンス測定を行うときは、React が本番モードで動作していることを確認してください。 -If you provide a custom `arePropsEqual` implementation, **you must compare every prop, including functions.** Functions often [close over](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) the props and state of parent components. If you return `true` when `oldProps.onClick !== newProps.onClick`, your component will keep "seeing" the props and state from a previous render inside its `onClick` handler, leading to very confusing bugs. +カスタムの `arePropsEqual` 実装を提供する場合、**関数を含むすべての prop を比較する必要があります**。しばしば関数はよく親コンポーネントにある props と state を[クロージャ内に閉じ込め](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)ます。`oldProps.onClick !== newProps.onClick` なのに `true` を返すと、コンポーネントは `onClick` ハンドラ内で以前のレンダー時の props と state を「見続ける」ことになり、非常に混乱するバグを引き起こします。 -Avoid doing deep equality checks inside `arePropsEqual` unless you are 100% sure that the data structure you're working with has a known limited depth. **Deep equality checks can become incredibly slow** and can freeze your app for many seconds if someone changes the data structure later. +対象のデータ構造が既知の有限の深さを持つことが 100% 確定している場合を除き、`arePropsEqual` 内で深い等価性チェックを行うことは避けてください。**深い等価性チェックは非常に遅くなる危険性がある**ため、誰かが後でデータ構造を変更することによりアプリが何秒間もフリーズしてしまう可能性があります。 --- -## Troubleshooting {/*troubleshooting*/} -### My component re-renders when a prop is an object, array, or function {/*my-component-rerenders-when-a-prop-is-an-object-or-array*/} +## トラブルシューティング {/*troubleshooting*/} +### props がオブジェクト・配列・関数の場合にコンポーネントが再レンダーされる {/*my-component-rerenders-when-a-prop-is-an-object-or-array*/} -React compares old and new props by shallow equality: that is, it considers whether each new prop is reference-equal to the old prop. If you create a new object or array each time the parent is re-rendered, even if the individual elements are each the same, React will still consider it to be changed. Similarly, if you create a new function when rendering the parent component, React will consider it to have changed even if the function has the same definition. To avoid this, [simplify props or memoize props in the parent component](#minimizing-props-changes). +React は古い props と新しい props とを浅く比較します。つまり、新しい props のそれぞれの値が古い props と参照ベースで等価であるかどうかを比較します。親が再レンダーのたびに新しいオブジェクトや配列を作成している場合、個々の要素が同じであっても、React は変更があったと考えます。同様に、親コンポーネントのレンダー時に新しい関数を作成すると、関数の定義が同じであっても、React はそれを別のものだと考えます。これを避けるためには、[親コンポーネントで props を単純化するか、または props をメモ化してください](#minimizing-props-changes)。 diff --git a/src/content/reference/react/useCallback.md b/src/content/reference/react/useCallback.md index 45e072cb1..8cae8df5b 100644 --- a/src/content/reference/react/useCallback.md +++ b/src/content/reference/react/useCallback.md @@ -214,28 +214,28 @@ function useCallback(fn, dependencies) { -#### すべてに useCallback を追加すべきか? {/*should-you-add-usecallback-everywhere*/} +#### あらゆる場所に useCallback を追加すべきか? {/*should-you-add-usecallback-everywhere*/} -あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かい(形状の移動など)場合、メモ化は非常に役立つかもしれません。 +あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。 `useCallback` で関数をキャッシュすることが有用なのはいくつかのケースに限られます。 -- それを [`memo`](/reference/react/memo) でラップされたコンポーネントに props として渡すケース。値が変わらないなら再レンダーをスキップしたいことでしょう。メモ化により、依存配列が変更された場合にのみ、コンポーネントが再レンダーされるようになります。 +- それを [`memo`](/reference/react/memo) でラップされたコンポーネントに props として渡すケース。この場合は、値が変化していない場合には再レンダーをスキップしたいでしょう。メモ化することで、依存値が異なる場合にのみコンポーネントを再レンダーさせることができます。 - あなたが渡している関数が、後で何らかのフックの依存値として使用されるケース。たとえば、他の `useCallback` でラップされた関数がそれに依存している、または [`useEffect`](/reference/react/useEffect) からこの関数に依存しているケースです。 -その他のケースで関数を `useCallback` でラップする利点はありません。それを行っても重大な害はないため、一部のチームは個々のケースについて考えず、可能な限り多くをメモ化することを選択します。欠点は、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。「毎回新しい」値がひとつあるだけで、コンポーネントのメモ化は全く機能しなくなってしまいます。 +これらのケース以外では、関数を `useCallback` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限り全てをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなることです。また、すべてのメモ化が効果的なわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。 `useCallback` は関数の*作成*を防ぐわけではないことに注意してください。あなたは常に関数を作成しています(それは問題ありません!)。しかし、何も変わらない場合、React はそれを無視し、キャッシュされた関数を返します。 **実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。 1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。 -1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうかのような頻繁に変わる state を、ツリーのトップやグローバル状態ライブラリに保持しないでください。 +1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。 1. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです! メモ化を追加するのではなく、バグを修正します。 1. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。 -1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化の代わりに、あるオブジェクトや関数をエフェクトの内側、あるいはコンポーネント外部に移動させる方が簡単なことがよくあります。 +1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。 -特定のインタラクションの遅延をまだ感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)どのコンポーネントをメモ化することが最も有益か確認し、必要な場所にメモ化を追加してください。上記の原則に従うことでコンポーネントのデバッグと理解が容易になるため、いかなる場合でも従っておくことは良いことです。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。 +それでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。 diff --git a/src/content/reference/react/useMemo.md b/src/content/reference/react/useMemo.md index f6ccea3f9..50d670a6d 100644 --- a/src/content/reference/react/useMemo.md +++ b/src/content/reference/react/useMemo.md @@ -113,7 +113,7 @@ function TodoList({ todos, tab, theme }) { #### 計算コストが高いかどうかを見分ける方法 {/*how-to-tell-if-a-calculation-is-expensive*/} -通常、数千のオブジェクトの作成やループをしていない限り、高コストな計算にはならないでしょう。より正確な判断をしたい場合は、コンソールログを追加して、コードの実行にかかった時間を計測することができます。 +一般的に、何千ものオブジェクトを作成したりループしたりしていない限り、おそらく高価ではありません。より確信を持ちたい場合は、コンソールログを追加して、コードの実行にかかった時間を計測することができます。 ```js {1,3} console.time('filter array'); @@ -121,7 +121,7 @@ const visibleTodos = filterTodos(todos, tab); console.timeEnd('filter array'); ``` -計測しているインタラクションを実行してみましょう(例えば、input 要素に文字を入力するなど)。すると、コンソールに、`filter array: 0.15ms` というようなログが表示されます。ログの時間が全体的にかなり長い(例えば `1ms` 以上)場合、その計算をメモ化することが有効かもしれません。実験として、計算を `useMemo` でラップしてみて、そのインタラクションに対する合計のログ時間が減少したかどうかを確認してみてください。 +測定したいユーザ操作(例えば、入力フィールドへのタイプ)を実行します。その後、コンソールに `filter array: 0.15ms` のようなログが表示されます。全体のログ時間がかなりの量(例えば `1ms` 以上)になる場合、その計算をメモ化する意味があるかもしれません。実験として `useMemo` で計算をラップしてみて、その操作に対する合計時間が減少したかどうかをログで確認できます。 ```js console.time('filter array'); @@ -131,37 +131,37 @@ const visibleTodos = useMemo(() => { console.timeEnd('filter array'); ``` -`useMemo` は、*最初の*レンダーを高速化することはできません。更新の場合に限って、不要な処理をスキップすることができます。 +`useMemo` は*初回*レンダーを高速化しません。更新時に不要な作業をスキップするときにのみ役立ちます。 -また、ほとんどの場合に、あなたが使っているマシンは、ユーザのマシンより高速に動作するであろうことを忘れてはいけません。そのため、意図的に処理速度を低下させてパフォーマンスをテストするのが良いでしょう。例えば Chrome では、[CPU Throttling](https://developer.chrome.com/blog/new-in-devtools-61/#throttling) オプションが提供されています。 +また、ほとんどの場合に、あなたが使っているマシンは、ユーザのマシンより高速に動作するであろうことを忘れてはいけません。そのため、意図的に処理速度を低下させてパフォーマンスをテストするのが良いでしょう。例えば、Chrome では [CPU スロットリング](https://developer.chrome.com/blog/new-in-devtools-61/#throttling)オプションが提供されています。 -加えて、開発時に行うパフォーマンス計測では、真に正確な結果は得られないことに注意してください。(例えば [Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントは本来 1 回レンダーされるところを、2 回レンダーされることがあります。)最も正確にパフォーマンスを計測するには、本番用にアプリをビルドし、ユーザが使用するようなデバイスでテストしてください。 +また、開発環境でのパフォーマンス測定では完全に正確な結果は得られないことに注意してください。(例えば、[Strict Mode](/reference/react/StrictMode) がオンの場合、各コンポーネントが 1 度ではなく 2 度レンダーされることがあります。)最も正確にパフォーマンスを計測するためには、アプリを本番環境用にビルドし、ユーザが持っているようなデバイスでテストしてください。 -#### 全ての場所で useMemo を使うべきか? {/*should-you-add-usememo-everywhere*/} +#### あらゆる場所に useMemo を追加すべきか? {/*should-you-add-usememo-everywhere*/} -このサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体を置き換えるなど)である場合、大抵はメモ化は不要です。一方、ドローイングエディタのように、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。 +あなたのアプリがこのサイトのように、ほとんどのインタラクションが大まかなもの(ページ全体やセクション全体の置き換えなど)である場合、メモ化は通常不要です。一方、あなたのアプリが描画エディタのようなもので、ほとんどのインタラクションが細かなもの(図形を移動させるなど)である場合、メモ化は非常に役に立つでしょう。 `useMemo` を利用した最適化が力を発揮するのは、以下のような、ほんの一部のケースに限られます。 - `useMemo` で行う計算が著しく遅く、かつ、その依存値がほとんど変化しない場合。 - 計算した値を、[`memo`](/reference/react/memo) でラップされたコンポーネントの props に渡す場合。この場合は、値が変化していない場合には再レンダーをスキップしたいでしょう。メモ化することで、依存値が異なる場合にのみコンポーネントを再レンダーさせることができます。 -- その値が、ほかのフックの依存値として利用される場合。例えば、別の `useMemo` の計算結果がその値に依存している場合や、[`useEffect`](/reference/react/useEffect) がその値に依存している場合などです。 +- その値が、後で何らかのフックの依存値として使用されるケース。例えば、別の `useMemo` の計算結果がその値に依存している場合や、[`useEffect`](/reference/react/useEffect) がその値に依存している場合などです。 -これらのケース以外では、計算を `useMemo` でラップすることにメリットはありません。しかし一方で、`useMemo` でラップすることによる大きなデメリットもありません。そのため、個別のケースを考えずに、可能な限り全てをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなるという点です。また、すべてのメモ化が有効であるわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。 +これらのケース以外では、計算を `useMemo` でラップすることにメリットはありません。それを行っても重大な害はないため、個別のケースを考えずに、可能な限り全てをメモ化するようにしているチームもあります。このアプローチのデメリットは、コードが読みにくくなるという点です。また、すべてのメモ化が有効であるわけではありません。例えば、毎回変化する値が 1 つ存在するだけで、コンポーネント全体のメモ化が無意味になってしまうこともあります。 -**実際には、次の原則に従うことで、メモ化の多くを無くすことができます。** +**実際には、以下のいくつかの原則に従うことで、多くのメモ化を不要にすることができます**。 -1. あるコンポーネントが他のコンポーネントを視覚的にラップする場合は、[JSX を子要素として受け取るようにしてください。](/learn/passing-props-to-a-component#passing-jsx-as-children)このようにすることで、ラッパーコンポーネントが自身の state を更新したときでも、子要素を再レンダーする必要がないと React が判断できます。 -1. ローカルな state を利用し、必要以上に [state をリフトアップする](/learn/sharing-state-between-components)ことは避けてください。例えば、フォームや、「アイテムがホバーされているかどうか」のような一時的な state は、ツリーのトップレベルやグローバルな state ライブラリに保持しないようにしてください。 -1. レンダーロジックを[純粋に保つ](/learn/keeping-components-pure)ようにしてください。コンポーネントの再レンダーによって、何か問題が起きたり、目に見える問題が引き起こされたりする場合、それはコンポーネントのバグです! メモ化をするのではなく、バグを修正してください。 -1. [state を更新する不要なエフェクト](/learn/you-might-not-need-an-effect)を避けてください。React アプリのパフォーマンス問題のほとんどは、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。 -2. [エフェクトから不要な依存値を削除してください。](/learn/removing-effect-dependencies)例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。 +1. コンポーネントが他のコンポーネントを視覚的にラップするときは、それが[子として JSX を受け入れるようにします](/learn/passing-props-to-a-component#passing-jsx-as-children)。これにより、ラッパコンポーネントが自身の state を更新しても、React はその子を再レンダーする必要がないことを認識します。 +1. ローカル state を優先し、必要以上に [state のリフトアップ](/learn/sharing-state-between-components)を行わないようにします。フォームや、アイテムがホバーされているかどうか、といった頻繁に変化する state は、ツリーのトップやグローバルの状態ライブラリに保持しないでください。 +1. [レンダーロジックを純粋に](/learn/keeping-components-pure)保ちます。コンポーネントの再レンダーが問題を引き起こしたり、何らかの目に見える視覚的な結果を生じたりする場合、それはあなたのコンポーネントのバグです! メモ化を追加するのではなく、バグを修正します。 +1. [state を更新する不要なエフェクトを避けてください](/learn/you-might-not-need-an-effect)。React アプリケーションのパフォーマンス問題の大部分は、エフェクト内での連鎖的な state 更新によってコンポーネントのレンダーが何度も引き起こされるために生じます。 +1. [エフェクトから不要な依存値をできるだけ削除します](/learn/removing-effect-dependencies)。例えば、メモ化する代わりに、オブジェクトや関数をエフェクトの中や外に移動させるだけで、簡単に解決できる場合があります。 -それでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラ](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)を利用して、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。私たちは、長期的な視点では、これらの問題を一気に解決することができる[細かいメモ化を自動化する](https://www.youtube.com/watch?v=lGEMwh32soc)方法を研究しています。 +それでも特定のインタラクションが遅いと感じる場合は、[React Developer Tools のプロファイラを使用して](https://legacy.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html)、どのコンポーネントでのメモ化が最も有効かを確認し、そこでメモ化を行いましょう。これらの原則を守ることで、コンポーネントのデバッグや理解が容易になるため、常に原則に従うことをおすすめします。長期的には、この問題を一挙に解決できる[自動的なメモ化](https://www.youtube.com/watch?v=lGEMwh32soc)について研究を行っています。