diff --git a/src/content/reference/react/memo.md b/src/content/reference/react/memo.md index 26fa9ed9c..25f4ff0ea 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` позволяет вам пропустить повторный рендер, когда пропсы не изменились. ``` 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`, чтобы получить *мемоизированную* версию вашего компонента. Данная мемоизированная версия компонента, как правило, не будет повторно рендериться, если не будет повторно рендериться родительский компонент, до тех пор, пока не изменятся пропсы. Но 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` не изменяет компонент, а возвращает его мемоизированную версию. Можно передать любой валидный React-компонент, включая функции и компоненты, обернутые в [`forwardRef`](/reference/react/forwardRef). -* **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`: функция, которая принимает два аргумента: предыдущие пропсы и новые пропсы компонента. Функция `arePropsEqual` возвращает `true`, если старые и новые пропсы равны: то есть, если компонент будет рендериться с одним и тем же результатом, и поведение с новыми пропсами будет таким же как и со старыми пропсами. Иначе вернется `false`. Обычно вам не нужно будет реализовывать эту функцию самостоятельно. По умолчанию, React будет сравнивать каждый проп при помощи [`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`, кроме тех случаев, когда React не будет его повторно рендерить, если родительский компонент тоже не был отрендерен повторно до тех пор, пока пропсы не изменились. --- -## Usage {/*usage*/} +## Использование {/*usage*/} -### Skipping re-rendering when props are unchanged {/*skipping-re-rendering-when-props-are-unchanged*/} +### ### Игнорирование повторного рендера, если пропсы не изменились {/*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`, вы можете создать компонент, который React не будет рендерить повторно, даже в случаях повторного рендера родительского компонента до тех пор, пока новые пропсы совпадают с предыдущими. Такой компонент называется *мемоизированным*. -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). То есть, должен возвращаться один и тот же результат при одних и тех же пропсах, состоянии, и при неизменном контексте. Когда вы используете `memo`, вы сообщаете React, что ваш компонент подчиняется этим требованиям, поэтому в повторном рендере нет необходимости до тех пор, пока пропсы не изменились. Но даже при использовании `memo`, ваш компонент будет рендериться повторно, если изменилось внутреннее состояние или контекст. -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): +В следующем примере, обратите внимание, что компонент `Greeting` рендерится повторно, когда изменяется `name` (из-за того, что это один из пропсов), но при изменении `address` повторного рендера не происходит (потому что он не передается `Greeting` в качестве пропа): @@ -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`, найдите основную проблему и сперва устраните ее. После этого можете добавить `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` является очень ценной, когда ваш компонент повторно рендерится с абсолютно одинаковыми пропсами, и повторная отрисовка очень дорогостоящая. Если при повторном рендере нет заметной задержки, использовать `memo` необязательно. Учтите, что `memo` будет абсолютно бесполезным решением, если передаваемые пропсы *всегда разные*, например, при передаче объектов или функций, которые создаются каждый раз с нуля. Именно поэтому, чаще всего вам нужно использовать [`useMemo`](/reference/react/useMemo#skipping-re-rendering-of-components) и [`useCallback`](/reference/react/useCallback#skipping-re-rendering-of-components) в паре с `memo`. -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`. Так же как и нет существенного вреда, некоторые команды выбирают не думать о конкретных случаях и использовать мемоизацию как можно чаще. Обратной стороной такого подхода--менее читаемый код. Также мемоизация не будет эффективна абсолютно во всех случаях: любого одного «всегда нового» значения достаточно, чтобы нарушить мемоизацию для всего компонента. -**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 в качестве children.](/learn/passing-props-to-a-component#passing-jsx-as-children) Это подход, когда оборачиваемый компонент обновляет собственное состояние и React знает, что дочерние компоненты не нуждаются в повторном рендере. +1. Предпочитайте использование внутреннего состояния и не [поднимайте состояние выше](/learn/sharing-state-between-components) чаще, чем это необходимо. Например, не сохраняйте кратковременное состояние как формы, независимо от того, находится ли ваш компонент на верхнем уровне вашего дерева или в глобальной библиотеке состояний. +1. Сохраняйте [чистой вашу логику рендера](/learn/keeping-components-pure). Если повторный рендер является причиной проблемы или создает заметный визуальный дефект, это ошибка в вашем компоненте! Постарайтесь исправить ошибку вместо использования мемоизации. +1. Избегайте [ненужных Эффектов, которые обновляют состояние](/learn/you-might-not-need-an-effect). Большинство проблем с производительностью в React приложении вызвано цепочкой обновлений в `useEffect`, которые заставляют ваши компоненты рендериться снова и снова. +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 profiler,](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*/} +### Обновление мемоизированного компонента с использованием состояния {/*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. +Даже если компонент мемоизирован, он все еще будет повторно рендериться, когда изменяется его внутреннее состояние. Мемоизация работает только с приходящими пропсами из родительского компонента. @@ -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. +Если вы установите переменную состояния как текущее значение, 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. +Даже если компонент мемоизирован, он все еще будет повторно рендериться, когда изменяется значение контекста, который использует компонент. Мемоизация работает только с приходящими пропсами из родительского компонента. @@ -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. +Разделите ваш компонент на два компонента, чтобы повторный рендер происходил только в случае, когда изменилась какая-то _часть_ контекста. Вызовите контекст в компоненте родителе и передайте значения ниже дочернему мемоизированному компоненту через пропсы. --- -### Minimizing props changes {/*minimizing-props-changes*/} +### Как минимизировать обновление пропсов {/*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`, ваш компонент будет повторно рендериться, если один из пропсов будет *поверхностно равен* пропу с предыдущего рендера. Это значит, что React сравнивает каждый проп компонента с пропом предыдущего рендера, используя сравнение [`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`, постарайтесь минимизировать количество обновлений пропсов. Например, если проп является объектом, можно предотвратить создание объекта каждый раз, используя [`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: +Самый лучший способ минимизировать обновление пропсов--это убедиться, что вы передаете компоненту минимальное количество информации в пропсах. Например, компонент может принимать конкретное значение, вместо целого объекта: ```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`. +В очень редких случаях невозможно минимизировать изменения пропсов компонента, который мы мемоизировали. В таком случае, вы можете определить пользовательскую функцию сравнения, которую React будет использовать, чтобы сравнивать старые и новые пропсы вместо использования стандартного поверхностного сравнения. Вы можете передать свою функцию сравнения в качестве второго аргумента в `memo`. Эта функция должна возвращать `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`, **вы должны сравнивать каждый проп, включая функции.** Чаще всего функции [замыкаются](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) в пропсах и состоянии родительского компонента. Если вы возвращаете `true` в случае `oldProps.onClick !== newProps.onClick`, ваш компонент все еще будет «видеть» пропсы и состоянии предыдущего рендера внутри обработчика `onClick`, что может приводить к очень запутанным багам. -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. +Старайтесь избегать глубокое сравнение внутри `arePropsEqual`, если вы не уверены на 100%, что структура данных, с которой вы работаете, имеет определенный уровень вложенности. **Глубокое сравнение может быть невероятно медленным** и замораживать ваше приложение на большой промежуток времени, если позже кто-то решит изменить структуру данных. --- -## 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*/} +### Мой компонент рендерится повторно, если проп это объект, массив или функция {/*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 поверхностно сравнивает старые и новые пропсы: это значит, что проверяется ссылка старого и нового пропа. Если вы создаете новый объект или массив, родительский компонент рендерится повторно, даже если конкретный элемент каждый раз такой же, React по прежнему будет считать, что он изменился. Тоже самое происходит, когда вы создаете функцию, при рендере родительского компонента, React будет считать, что она изменилась даже, если определение функции осталось прежним. Чтобы избежать такого поведения, [делайте пропсы проще или мемоизируйте пропсы родительского компонента](#minimizing-props-changes).