From ccb9f2fd138613aa7290356726d949e96c0f06b0 Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Thu, 14 Feb 2019 18:15:50 +1100 Subject: [PATCH 1/9] Translate HOC --- TRANSLATION.md | 1 + content/docs/higher-order-components.md | 202 ++++++++++++------------ 2 files changed, 101 insertions(+), 102 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index 49b053351..bbfbd10d0 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -75,6 +75,7 @@ | development | разработка | | development mode | режим разработки | | developer tools | инструменты разработки | +| display name | имя отображения | | DOM container | DOM-контейнер | | effect | эффект | | error | ошибка | diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 63da3ef86..ccd408135 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -1,32 +1,33 @@ --- id: higher-order-components -title: Higher-Order Components +title: Компоненты высшего порядка permalink: docs/higher-order-components.html --- A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature. +Компонент высшего порядка (Higher-Order Component (HOC)) -- это один из продвинутых инструментов для повторного использования логики. HOC не являются частью API React, но часто применяются из-за композиционной природы компонентов. -Concretely, **a higher-order component is a function that takes a component and returns a new component.** +Говоря просто, **компонент высшего порядка -- это функция, которя принимает компонент и возвращает новый компонент.** ```js const EnhancedComponent = higherOrderComponent(WrappedComponent); ``` -Whereas a component transforms props into UI, a higher-order component transforms a component into another component. +Если обычный компонент преобразует пропсы в UI, то компонент высшего порядка преобразует компонент в другой компонент. -HOCs are common in third-party React libraries, such as Redux's [`connect`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) and Relay's [`createFragmentContainer`](http://facebook.github.io/relay/docs/en/fragment-container.html). +HOC часто встречаются в сторонних библиотеках, например [`connect`](https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) в Redux и [`createFragmentContainer`](http://facebook.github.io/relay/docs/en/fragment-container.html) в Relay. -In this document, we'll discuss why higher-order components are useful, and how to write your own. +В этой главе мы обсудим чем полезны компоненты высшего порядка и как их создавать. -## Use HOCs For Cross-Cutting Concerns {#use-hocs-for-cross-cutting-concerns} +## HOC для сквозной функциональности {#use-hocs-for-cross-cutting-concerns} -> **Note** +> **Примечание** > -> We previously recommended mixins as a way to handle cross-cutting concerns. We've since realized that mixins create more trouble than they are worth. [Read more](/blog/2016/07/13/mixins-considered-harmful.html) about why we've moved away from mixins and how you can transition your existing components. +> В прошлом мы рекомендовали миксины для реализации сквозной функциональности, но со временем выяснилось, что от них больше вреда, чем пользы. [Узнайте](/blog/2016/07/13/mixins-considered-harmful.html), почему мы решили убрать миксины и как переписать старые компоненты. -Components are the primary unit of code reuse in React. However, you'll find that some patterns aren't a straightforward fit for traditional components. +Традиционные компоненты подрузамевают многократное использование, но не позволяют с легкостью решить некоторые проблемы. -For example, say you have a `CommentList` component that subscribes to an external data source to render a list of comments: +Рассмотрим пример `CommentList`, который получает список комментариев из внешнего источника данных и отображает их: ```js class CommentList extends React.Component { @@ -34,23 +35,23 @@ class CommentList extends React.Component { super(props); this.handleChange = this.handleChange.bind(this); this.state = { - // "DataSource" is some global data source + // "DataSource" -- произвольный глобальный источник данных comments: DataSource.getComments() }; } componentDidMount() { - // Subscribe to changes + // Подписаться на оповещения DataSource.addChangeListener(this.handleChange); } componentWillUnmount() { - // Clean up listener + // Отписаться от оповещений DataSource.removeChangeListener(this.handleChange); } handleChange() { - // Update component state whenever the data source changes + // Сохранить комментарии из внешнего источника в локальном состоянии this.setState({ comments: DataSource.getComments() }); @@ -68,7 +69,7 @@ class CommentList extends React.Component { } ``` -Later, you write a component for subscribing to a single blog post, which follows a similar pattern: +Теперь мы решили реализовать новый компонент, который отслеживает изменения конкретной публикации и повторяет уже знакомый нам шаблон: ```js class BlogPost extends React.Component { @@ -100,15 +101,15 @@ class BlogPost extends React.Component { } ``` -`CommentList` and `BlogPost` aren't identical — they call different methods on `DataSource`, and they render different output. But much of their implementation is the same: +Разница между`CommentList` и `BlogPost` в том, что они вызывают разные методы `DataSource` и рендерят разный вывод, но есть и много общего: -- On mount, add a change listener to `DataSource`. -- Inside the listener, call `setState` whenever the data source changes. -- On unmount, remove the change listener. +- Оба компонента подписываются на оповещения от `DataSource` при монтировании. +- Оба меняют локальное состояние когда меняется `DataSource`. +- Оба отписываются от `DataSource` при размонтировании. -You can imagine that in a large app, this same pattern of subscribing to `DataSource` and calling `setState` will occur over and over again. We want an abstraction that allows us to define this logic in a single place and share it across many components. This is where higher-order components excel. +Можете представить, что в больших приложениях связка «подписаться на `DataSource`, затем вызвать `setState`» повторяется очень часто. Было бы здорово абстрагировать эту функиональность и использовать ее в других компонентах. -We can write a function that creates components, like `CommentList` and `BlogPost`, that subscribe to `DataSource`. The function will accept as one of its arguments a child component that receives the subscribed data as a prop. Let's call the function `withSubscription`: +Давайте реализуем функцию `withSubscription` -- она будет создавать компоненты и подписывать их на обновления `DataSource` (наподобие `CommentList` и `BlogPost`). Функция будет принимать оборачиваемый компонент и через пропсы передавать ему новые данные: ```js const CommentListWithSubscription = withSubscription( @@ -122,14 +123,14 @@ const BlogPostWithSubscription = withSubscription( ); ``` -The first parameter is the wrapped component. The second parameter retrieves the data we're interested in, given a `DataSource` and the current props. +Первый параметр -- это оборачиваемый компонент. Второй -- функция, которая извлекает нужные нам данные, она получает `DataSource` и текущие пропсы. -When `CommentListWithSubscription` and `BlogPostWithSubscription` are rendered, `CommentList` and `BlogPost` will be passed a `data` prop with the most current data retrieved from `DataSource`: +Когда `CommentListWithSubscription` и `BlogPostWithSubscription` рендерятся, они передают в `CommentList` и `BlogPost` обновлённые данные `DataSource` через проп `data`: ```js -// This function takes a component... +// Это функция принимает компонент... function withSubscription(WrappedComponent, selectData) { - // ...and returns another component... + // ...и возвращает другой компонент... return class extends React.Component { constructor(props) { super(props); @@ -140,7 +141,7 @@ function withSubscription(WrappedComponent, selectData) { } componentDidMount() { - // ... that takes care of the subscription... + // ...который подписывется на оповещения... DataSource.addChangeListener(this.handleChange); } @@ -155,83 +156,80 @@ function withSubscription(WrappedComponent, selectData) { } render() { - // ... and renders the wrapped component with the fresh data! - // Notice that we pass through any additional props + // ... и рендерит оборачиваемый компонент со свежими данными! + // Обратите внимание, что мы передаем остальные пропсы return ; } }; } ``` -Note that a HOC doesn't modify the input component, nor does it use inheritance to copy its behavior. Rather, a HOC *composes* the original component by *wrapping* it in a container component. A HOC is a pure function with zero side-effects. +Заметьте, что HOC ничего не меняет и не наследует поведение оборачиваемого компонента, вместо этого HOC *оборачивает* оригинальный компонент в контейнер посредством *композиции*. HOC является чистой функцией без посторонних эффектов. -And that's it! The wrapped component receives all the props of the container, along with a new prop, `data`, which it uses to render its output. The HOC isn't concerned with how or why the data is used, and the wrapped component isn't concerned with where the data came from. +Вот и все! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп `data`. Для HOC не важно как будут использоваться данные, а оборачиваемому компоненту не важно откуда они берутся. -Because `withSubscription` is a normal function, you can add as many or as few arguments as you like. For example, you may want to make the name of the `data` prop configurable, to further isolate the HOC from the wrapped component. Or you could accept an argument that configures `shouldComponentUpdate`, or one that configures the data source. These are all possible because the HOC has full control over how the component is defined. +Так как `withSubscription` -- это обычная функция, то мы можем убрать или добавить любое количество аргументов. Например, мы могли бы сделать конфигурируемым название пропа `data` и еще больше изолировать HOC от оборачиваемого компонента. Также мы можем добавить аргумент для конфигурации `shouldComponentUpdate` или источника данных. Все это возможно, потому что HOC полностью контролирует процесс создания компонента. -Like components, the contract between `withSubscription` and the wrapped component is entirely props-based. This makes it easy to swap one HOC for a different one, as long as they provide the same props to the wrapped component. This may be useful if you change data-fetching libraries, for example. +Взаимодействие между `withSubscription` и оборачиваемым компонентом осуществляется с помощью пропсов, так же, как и между обычными компонентами. Благодаря этому мы можем с легкостью заменить один HOC на другой, при условии, что они передают одни и те же пропсы в оборачиваемый компонент. Это может пригодиться если, например, мы решим поменять библиотеку получения данных. -## Don't Mutate the Original Component. Use Composition. {#dont-mutate-the-original-component-use-composition} +## Не мутируйте оборачиваемый компонент. Используйте композицию. {#dont-mutate-the-original-component-use-composition} -Resist the temptation to modify a component's prototype (or otherwise mutate it) inside a HOC. +Не поддавайтесь соблазну менять прототип компонента (или мутировать его любым другим способом) внутри HOC. ```js function logProps(InputComponent) { InputComponent.prototype.componentWillReceiveProps = function(nextProps) { - console.log('Current props: ', this.props); - console.log('Next props: ', nextProps); + console.log('Текущие пропсы: ', this.props); + console.log('Следующие пропсы: ', nextProps); }; - // The fact that we're returning the original input is a hint that it has - // been mutated. + // Если мы возвращаем оборачиваемый компонент, значит, наверняка мы его изменили return InputComponent; } -// EnhancedComponent will log whenever props are received +// EnhancedComponent будет печатать в консоль при каждом изменении пропсов const EnhancedComponent = logProps(InputComponent); ``` -There are a few problems with this. One is that the input component cannot be reused separately from the enhanced component. More crucially, if you apply another HOC to `EnhancedComponent` that *also* mutates `componentWillReceiveProps`, the first HOC's functionality will be overridden! This HOC also won't work with function components, which do not have lifecycle methods. +В приведенном выше примере мы не можем повторно использовать `InputComponent` отдельно от `EnhancedComponent`. Важнее то, что если мы захотим обернуть `EnhancedComponent` в другой HOC, который *тоже* меняет `componentWillReceiveProps`, то мы сотрем функциональность заданную первым HOC! Более того, `EnhancedComponent` не работает с функциональными компонентами потому, что у них отсутствуют методы жизненного цикла. -Mutating HOCs are a leaky abstraction—the consumer must know how they are implemented in order to avoid conflicts with other HOCs. +Мутирующие HOC являются хрупкой абстракцией, они конфликтуют с другими HOC, мы не сможем просто применять их без того, чтобы знать что именно они меняют. -Instead of mutation, HOCs should use composition, by wrapping the input component in a container component: +Вместо мутации, компоненты высшего порядка должны применять композицию, оборачивая компонент в контейнер: ```js function logProps(WrappedComponent) { return class extends React.Component { componentWillReceiveProps(nextProps) { - console.log('Current props: ', this.props); - console.log('Next props: ', nextProps); + console.log('Текущие пропсы: ', this.props); + console.log('Следующие пропсы: ', nextProps); } render() { - // Wraps the input component in a container, without mutating it. Good! + // Оборачиваем компонент в контейнер без мутаций. Супер! return ; } } } ``` -This HOC has the same functionality as the mutating version while avoiding the potential for clashes. It works equally well with class and function components. And because it's a pure function, it's composable with other HOCs, or even with itself. +Этот HOC обладает такой же функциональностью, как и предыдущий, но не создает конфликтов с другими HOC и работает как с функциональными, так и с классовыми компонентами. Более того, HOC реализован с помощью чистой функции, поэтому его можно совмещать с другими HOC, или даже самого с собой. -You may have noticed similarities between HOCs and a pattern called **container components**. Container components are part of a strategy of separating responsibility between high-level and low-level concerns. Containers manage things like subscriptions and state, and pass props to components that handle things like rendering UI. HOCs use containers as part of their implementation. You can think of HOCs as parameterized container component definitions. +Возможно, вы уже заметили сходство между HOC и **компонентами-контейнерами**. Напомним, что при помощи контейнеров мы обычно разделяем общую функциональность от частной. Например, в контейнере мы будем управлять внутренним состоянием или подпиской на внешние ресурсы, и через пропсы передавать данные в компоненты, ответственные за рендер UI. При реализации HOС мы тоже используем контейнеры. Можно сказать что HOC -- это инструмент для параметризированного создания контейнеров. -## Convention: Pass Unrelated Props Through to the Wrapped Component {#convention-pass-unrelated-props-through-to-the-wrapped-component} +## Конвенция: передавайте посторонние пропсы оборачиваемому компоненту{#convention-pass-unrelated-props-through-to-the-wrapped-component} -HOCs add features to a component. They shouldn't drastically alter its contract. It's expected that the component returned from a HOC has a similar interface to the wrapped component. +HOC добавляют компонентам функциональность, но они не должны менять их оригинальное предназначение. Ожидается, что интерфейс компонента, который вы возвращаете из HOC, будет похож на интерфейс оборачиваемого компонента. -HOCs should pass through props that are unrelated to its specific concern. Most HOCs contain a render method that looks something like this: +Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер метод большинства HOC похож следующий: ```js render() { - // Filter out extra props that are specific to this HOC and shouldn't be - // passed through + // Отфильтруйте пропсы применимые только к этому HOC и которые не нужно передавать дальше const { extraProp, ...passThroughProps } = this.props; - // Inject props into the wrapped component. These are usually state values or - // instance methods. + // Добавьте новые пропсы в оборачиваемый компонент. Обычно мы передаем значения состояния или методы экземпляра const injectedProp = someStateOrInstanceMethod; - // Pass props to wrapped component + // Передайте пропсы в оборачиваемый компонент return ( Component`. Functions whose output type is the same as its input type are really easy to compose together. +Такая форма может показаться запутанной и ненужной, но есть и преимущества. Вызов `connect` возвращает HOC с подписью `Component => Component`. Функции с одинаковым типом результата и единственного аргумента легко совмещаются в композиции. ```js -// Instead of doing this... +// Вместо этого... const EnhancedComponent = withRouter(connect(commentSelector)(WrappedComponent)) -// ... you can use a function composition utility -// compose(f, g, h) is the same as (...args) => f(g(h(...args))) +// ... вы можете воспользоваться вспомогательной совмещающей функцией +// compose(f, g, h) идентичен (...args) => f(g(h(...args))) const enhance = compose( - // These are both single-argument HOCs + // Оба параметра являются HOC и принимают один единственный аргумент withRouter, connect(commentSelector) ) const EnhancedComponent = enhance(WrappedComponent) ``` -(This same property also allows `connect` and other enhancer-style HOCs to be used as decorators, an experimental JavaScript proposal.) +(Поэтому мы можем использовать `connect`, и другие расширяющие функциональность HOC, в качестве экспериментальных JavaScript декораторов.) -The `compose` utility function is provided by many third-party libraries including lodash (as [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](http://redux.js.org/docs/api/compose.html), and [Ramda](http://ramdajs.com/docs/#compose). +Вы можете найти вспомогательную функцию `compose` во многих сторонних библиотеках, включая lodash (под названием [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](http://redux.js.org/docs/api/compose.html), и [Ramda](http://ramdajs.com/docs/#compose). -## Convention: Wrap the Display Name for Easy Debugging {#convention-wrap-the-display-name-for-easy-debugging} +## Конвенция: добавьте имя отображения для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} -The container components created by HOCs show up in the [React Developer Tools](https://github.com/facebook/react-devtools) like any other component. To ease debugging, choose a display name that communicates that it's the result of a HOC. +Созданные HOC компоненты-контейнеры отображаются в консоли [инструментов разработки React](https://github.com/facebook/react-devtools) наряду с другими компонентами. Для более легкой отладки вы можете задать имя, которое подскажет, что определенный компонент был создан с помощью HOC. -The most common technique is to wrap the display name of the wrapped component. So if your higher-order component is named `withSubscription`, and the wrapped component's display name is `CommentList`, use the display name `WithSubscription(CommentList)`: +Самый распростроненный способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, задайте имя отображения `WithSubscription(CommentList)`: ```js function withSubscription(WrappedComponent) { @@ -313,61 +311,61 @@ function getDisplayName(WrappedComponent) { } ``` +## Предостережения {#caveats} -## Caveats {#caveats} - -Higher-order components come with a few caveats that aren't immediately obvious if you're new to React. +Вы можете столкнуться с неочевидными проблемами когда работаете с компонентами высшего порядка. -### Don't Use HOCs Inside the render Method {#dont-use-hocs-inside-the-render-method} +### Не используйте HOC внутри рендер метода {#dont-use-hocs-inside-the-render-method} -React's diffing algorithm (called reconciliation) uses component identity to determine whether it should update the existing subtree or throw it away and mount a new one. If the component returned from `render` is identical (`===`) to the component from the previous render, React recursively updates the subtree by diffing it with the new one. If they're not equal, the previous subtree is unmounted completely. +Алгоритм сравнения React (или согласование (reconciliation)) использует тождественность компонентов чтобы определить нужно ли обновить существующее под-дерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать под-дерево. Если компоненты не равны, React полностью удалит и заменит старое под-дерево. Normally, you shouldn't need to think about this. But it matters for HOCs because it means you can't apply a HOC to a component within the render method of a component: +Обычно нас это не беспокоит. Однако, важно учитывать что мы не можем применять компоненты высшего порядка внутри рендер метода компонента: ```js render() { - // A new version of EnhancedComponent is created on every render + // Мы создаем новую версию EnhancedComponent при каждом рендере // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); - // That causes the entire subtree to unmount/remount each time! + // Мы каждый раз размонтируем и монтируем целиком все под-дерево! return ; } ``` -The problem here isn't just about performance — remounting a component causes the state of that component and all of its children to be lost. +Проблема не только в производительности. Повторное монтирование компонента обнуляет его состояние, а также состояние его дочерних компонентов. -Instead, apply HOCs outside the component definition so that the resulting component is created only once. Then, its identity will be consistent across renders. This is usually what you want, anyway. +Не применяйте HOC в определении другого компонент. Сначала нужно отдельно получить компонент из HOC, и только потом использовать его. Таким образом React будет сравнивать один и тот же компонент при повторном рендере. -In those rare cases where you need to apply a HOC dynamically, you can also do it inside a component's lifecycle methods or its constructor. +При необходимости, в редких случаях, можно динамически применять HOC в методах жизненного цикла или в конструкторе компонента. -### Static Methods Must Be Copied Over {#static-methods-must-be-copied-over} +### Копируйте статические методы {#static-methods-must-be-copied-over} -Sometimes it's useful to define a static method on a React component. For example, Relay containers expose a static method `getFragment` to facilitate the composition of GraphQL fragments. +Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данныз GraphQL. -When you apply a HOC to a component, though, the original component is wrapped with a container component. That means the new component does not have any of the static methods of the original component. +Когда мы применяем HOC, мы заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статичных методов оригинального компонента. ```js -// Define a static method +// Определим статичный метод WrappedComponent.staticMethod = function() {/*...*/} -// Now apply a HOC +// Теперь применим HOC const EnhancedComponent = enhance(WrappedComponent); -// The enhanced component has no static method +// У расширенного компонента нет статичных методов typeof EnhancedComponent.staticMethod === 'undefined' // true ``` -To solve this, you could copy the methods onto the container before returning it: +Скопируйте недостающие методы в контейнер: ```js function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} - // Must know exactly which method(s) to copy :( + // Мы должны точно знать какие методы копировать :( Enhance.staticMethod = WrappedComponent.staticMethod; return Enhance; } ``` -However, this requires you to know exactly which methods need to be copied. You can use [hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics) to automatically copy all non-React static methods: +К сожалению, вы должны точно знать какие методы копировать. Вы можете воспользоваться [hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics), чтобы автоматически скопировать не связанные с React статичные методы: ```js import hoistNonReactStatic from 'hoist-non-react-statics'; @@ -378,22 +376,22 @@ function enhance(WrappedComponent) { } ``` -Another possible solution is to export the static method separately from the component itself. +Другое возможное решение -- экспортировать статичные методы отдельно от компонента. ```js -// Instead of... +// Вместо... MyComponent.someFunction = someFunction; export default MyComponent; -// ...export the method separately... +// ...отдельно экспортируйте метод... export { someFunction }; -// ...and in the consuming module, import both +// ...в модуле-потребителе мы можем использовать оба экспорта import MyComponent, { someFunction } from './MyComponent.js'; ``` -### Refs Aren't Passed Through {#refs-arent-passed-through} +### Рефы не передаются {#refs-arent-passed-through} -While the convention for higher-order components is to pass through all props to the wrapped component, this does not work for refs. That's because `ref` is not really a prop — like `key`, it's handled specially by React. If you add a ref to an element whose component is the result of a HOC, the ref refers to an instance of the outermost container component, not the wrapped component. +По конвенции, компоненты высшего порядка и передают все пропсы оборачеваемому компоненту, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерахрии контейнера, а не на оборачиваемый копмонент. -The solution for this problem is to use the `React.forwardRef` API (introduced with React 16.3). [Learn more about it in the forwarding refs section](/docs/forwarding-refs.html). \ No newline at end of file +Вы можете решить эту проблему с помощью `React.forwardRef` API (представлен в React 16.3). [Узнать больше в главе Пересылка рефов](/docs/forwarding-refs.html). \ No newline at end of file From 22bedb3a655af652214f099de1e2d94e38bb4d80 Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Tue, 19 Feb 2019 12:04:40 +1100 Subject: [PATCH 2/9] Add nav translation for HOC --- content/docs/nav.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/nav.yml b/content/docs/nav.yml index f6587e288..88ba89cbd 100644 --- a/content/docs/nav.yml +++ b/content/docs/nav.yml @@ -50,7 +50,7 @@ - id: fragments title: Fragments - id: higher-order-components - title: Higher-Order Components + title: Компоненты высшего порядка - id: integrating-with-other-libraries title: Integrating with Other Libraries - id: jsx-in-depth From 083fef68a7bc9ff7fbf56690e350ea785b8201be Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Tue, 19 Feb 2019 12:06:22 +1100 Subject: [PATCH 3/9] Apply small fixes to HOC translation --- content/docs/higher-order-components.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index b162f5b31..113df2be2 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -7,7 +7,7 @@ permalink: docs/higher-order-components.html A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature. Компонент высшего порядка (Higher-Order Component (HOC)) -- это один из продвинутых инструментов для повторного использования логики. HOC не являются частью API React, но часто применяются из-за композиционной природы компонентов. -Говоря просто, **компонент высшего порядка -- это функция, которя принимает компонент и возвращает новый компонент.** +Говоря просто, **компонент высшего порядка -- это функция, которая принимает компонент и возвращает новый компонент.** ```js const EnhancedComponent = higherOrderComponent(WrappedComponent); @@ -107,7 +107,7 @@ class BlogPost extends React.Component { - Оба меняют локальное состояние когда меняется `DataSource`. - Оба отписываются от `DataSource` при размонтировании. -Можете представить, что в больших приложениях связка «подписаться на `DataSource`, затем вызвать `setState`» повторяется очень часто. Было бы здорово абстрагировать эту функиональность и использовать ее в других компонентах. +Можете представить, что в больших приложениях связка «подписаться на `DataSource`, затем вызвать `setState`» повторяется очень часто. Было бы здорово абстрагировать эту функциональность и использовать ее в других компонентах. Давайте реализуем функцию `withSubscription` -- она будет создавать компоненты и подписывать их на обновления `DataSource` (наподобие `CommentList` и `BlogPost`). Функция будет принимать оборачиваемый компонент и через пропсы передавать ему новые данные: @@ -219,7 +219,7 @@ function logProps(WrappedComponent) { HOC добавляют компонентам функциональность, но они не должны менять их оригинальное предназначение. Ожидается, что интерфейс компонента, который вы возвращаете из HOC, будет похож на интерфейс оборачиваемого компонента. -Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер метод большинства HOC похож следующий: +Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер метод большинства HOC похож на следующий: ```js render() { @@ -262,7 +262,7 @@ const CommentWithRelay = Relay.createContainer(Comment, config); const ConnectedComment = connect(commentSelector, commentActions)(CommentList); ``` -*Удивлены?* Давайте разберем эту строку на части. +*Удивлены?* Давайте разберем эту строку по частям. ```js // Вызов функции connect возвращает другую функцию @@ -334,13 +334,13 @@ render() { Проблема не только в производительности. Повторное монтирование компонента обнуляет его состояние, а также состояние его дочерних компонентов. -Не применяйте HOC в определении другого компонент. Сначала нужно отдельно получить компонент из HOC, и только потом использовать его. Таким образом React будет сравнивать один и тот же компонент при повторном рендере. +Не применяйте HOC в определении другого компонента. Сначала нужно отдельно получить компонент из HOC, и только потом использовать его. Таким образом React будет сравнивать один и тот же компонент при повторном рендере. При необходимости, в редких случаях, можно динамически применять HOC в методах жизненного цикла или в конструкторе компонента. ### Копируйте статические методы {#static-methods-must-be-copied-over} -Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данныз GraphQL. +Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данных GraphQL. Когда мы применяем HOC, мы заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статичных методов оригинального компонента. @@ -392,10 +392,6 @@ import MyComponent, { someFunction } from './MyComponent.js'; ### Рефы не передаются {#refs-arent-passed-through} -По конвенции, компоненты высшего порядка и передают все пропсы оборачеваемому компоненту, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерахрии контейнера, а не на оборачиваемый копмонент. +По конвенции, компоненты высшего порядка передают оборачеваемому компоненту все пропсы, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерахрии контейнера, а не на оборачиваемый компонент. -<<<<<<< HEAD Вы можете решить эту проблему с помощью `React.forwardRef` API (представлен в React 16.3). [Узнать больше в главе Пересылка рефов](/docs/forwarding-refs.html). -======= -The solution for this problem is to use the `React.forwardRef` API (introduced with React 16.3). [Learn more about it in the forwarding refs section](/docs/forwarding-refs.html). ->>>>>>> upstream/master From 2fa6ef420f35832a12b547cf98acf5c51d3ff41f Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Tue, 19 Feb 2019 16:23:06 +1100 Subject: [PATCH 4/9] Rephrase intro sentence --- content/docs/higher-order-components.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 113df2be2..3213adafe 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -4,8 +4,7 @@ title: Компоненты высшего порядка permalink: docs/higher-order-components.html --- -A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React's compositional nature. -Компонент высшего порядка (Higher-Order Component (HOC)) -- это один из продвинутых инструментов для повторного использования логики. HOC не являются частью API React, но часто применяются из-за композиционной природы компонентов. +Компонент высшего порядка (Higher-Order Component, HOC) -- это один из продвинутых способов для повторного использования логики. HOC не являются частью API React, но часто применяются из-за композиционной природы компонентов. Говоря просто, **компонент высшего порядка -- это функция, которая принимает компонент и возвращает новый компонент.** From 9029364ccaf283937d6e92587a505a0fbc18b6e7 Mon Sep 17 00:00:00 2001 From: Leonid Fedorov Date: Tue, 19 Feb 2019 10:21:22 -0800 Subject: [PATCH 5/9] Apply suggestions from code review Co-Authored-By: agoldis --- content/docs/higher-order-components.md | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 3213adafe..362c19997 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -24,7 +24,7 @@ HOC часто встречаются в сторонних библиотека > > В прошлом мы рекомендовали миксины для реализации сквозной функциональности, но со временем выяснилось, что от них больше вреда, чем пользы. [Узнайте](/blog/2016/07/13/mixins-considered-harmful.html), почему мы решили убрать миксины и как переписать старые компоненты. -Традиционные компоненты подрузамевают многократное использование, но не позволяют с легкостью решить некоторые проблемы. +Традиционные компоненты подразумевают многократное использование, но не позволяют с лёгкостью решить некоторые проблемы. Рассмотрим пример `CommentList`, который получает список комментариев из внешнего источника данных и отображает их: @@ -165,11 +165,11 @@ function withSubscription(WrappedComponent, selectData) { Заметьте, что HOC ничего не меняет и не наследует поведение оборачиваемого компонента, вместо этого HOC *оборачивает* оригинальный компонент в контейнер посредством *композиции*. HOC является чистой функцией без посторонних эффектов. -Вот и все! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп `data`. Для HOC не важно как будут использоваться данные, а оборачиваемому компоненту не важно откуда они берутся. +Вот и всё! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп `data`. Для HOC не важно как будут использоваться данные, а оборачиваемому компоненту не важно откуда они берутся. -Так как `withSubscription` -- это обычная функция, то мы можем убрать или добавить любое количество аргументов. Например, мы могли бы сделать конфигурируемым название пропа `data` и еще больше изолировать HOC от оборачиваемого компонента. Также мы можем добавить аргумент для конфигурации `shouldComponentUpdate` или источника данных. Все это возможно, потому что HOC полностью контролирует процесс создания компонента. +Так как `withSubscription` -- это обычная функция, то мы можем убрать или добавить любое количество аргументов. Например, мы могли бы сделать конфигурируемым название пропа `data` и ещё больше изолировать HOC от оборачиваемого компонента. Также мы можем добавить аргумент для конфигурации `shouldComponentUpdate` или источника данных. Всё это возможно, потому что HOC полностью контролирует процесс создания компонента. -Взаимодействие между `withSubscription` и оборачиваемым компонентом осуществляется с помощью пропсов, так же, как и между обычными компонентами. Благодаря этому мы можем с легкостью заменить один HOC на другой, при условии, что они передают одни и те же пропсы в оборачиваемый компонент. Это может пригодиться если, например, мы решим поменять библиотеку получения данных. +Взаимодействие между `withSubscription` и оборачиваемым компонентом осуществляется с помощью пропсов, так же, как и между обычными компонентами. Благодаря этому мы можем с лёгкостью заменить один HOC на другой, при условии, что они передают одни и те же пропсы в оборачиваемый компонент. Это может пригодиться если, например, мы решим поменять библиотеку получения данных. ## Не мутируйте оборачиваемый компонент. Используйте композицию. {#dont-mutate-the-original-component-use-composition} @@ -189,7 +189,7 @@ function logProps(InputComponent) { const EnhancedComponent = logProps(InputComponent); ``` -В приведенном выше примере мы не можем повторно использовать `InputComponent` отдельно от `EnhancedComponent`. Важнее то, что если мы захотим обернуть `EnhancedComponent` в другой HOC, который *тоже* меняет `componentWillReceiveProps`, то мы сотрем функциональность заданную первым HOC! Более того, `EnhancedComponent` не работает с функциональными компонентами потому, что у них отсутствуют методы жизненного цикла. +В приведённом выше примере мы не можем повторно использовать `InputComponent` отдельно от `EnhancedComponent`. Важнее то, что если мы захотим обернуть `EnhancedComponent` в другой HOC, который *тоже* меняет `componentWillReceiveProps`, то мы сотрем функциональность заданную первым HOC! Более того, `EnhancedComponent` не работает с функциональными компонентами потому, что у них отсутствуют методы жизненного цикла. Мутирующие HOC являются хрупкой абстракцией, они конфликтуют с другими HOC, мы не сможем просто применять их без того, чтобы знать что именно они меняют. @@ -210,7 +210,7 @@ function logProps(WrappedComponent) { } ``` -Этот HOC обладает такой же функциональностью, как и предыдущий, но не создает конфликтов с другими HOC и работает как с функциональными, так и с классовыми компонентами. Более того, HOC реализован с помощью чистой функции, поэтому его можно совмещать с другими HOC, или даже самого с собой. +Этот HOC обладает такой же функциональностью, как и предыдущий, но не создаёт конфликтов с другими HOC и работает как с функциональными, так и с классовыми компонентами. Более того, HOC реализован с помощью чистой функции, поэтому его можно совмещать с другими HOC, или даже самого с собой. Возможно, вы уже заметили сходство между HOC и **компонентами-контейнерами**. Напомним, что при помощи контейнеров мы обычно разделяем общую функциональность от частной. Например, в контейнере мы будем управлять внутренним состоянием или подпиской на внешние ресурсы, и через пропсы передавать данные в компоненты, ответственные за рендер UI. При реализации HOС мы тоже используем контейнеры. Можно сказать что HOC -- это инструмент для параметризированного создания контейнеров. @@ -254,14 +254,14 @@ const NavbarWithRouter = withRouter(Navbar); const CommentWithRelay = Relay.createContainer(Comment, config); ``` -Самый распростронённый способ вызова HOC выглядит так: +Самый распространённый способ вызова HOC выглядит так: ```js // `connect` из React Redux const ConnectedComment = connect(commentSelector, commentActions)(CommentList); ``` -*Удивлены?* Давайте разберем эту строку по частям. +*Удивлены?* Давайте разберём эту строку по частям. ```js // Вызов функции connect возвращает другую функцию @@ -294,9 +294,9 @@ const EnhancedComponent = enhance(WrappedComponent) ## Конвенция: добавьте имя отображения для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} -Созданные HOC компоненты-контейнеры отображаются в консоли [инструментов разработки React](https://github.com/facebook/react-devtools) наряду с другими компонентами. Для более легкой отладки вы можете задать имя, которое подскажет, что определенный компонент был создан с помощью HOC. +Созданные HOC компоненты-контейнеры отображаются в консоли [инструментов разработки React](https://github.com/facebook/react-devtools) наряду с другими компонентами. Для более лёгкой отладки вы можете задать имя, которое подскажет, что определенный компонент был создан с помощью HOC. -Самый распростроненный способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, задайте имя отображения `WithSubscription(CommentList)`: +Самый распространённый способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, задайте имя отображения `WithSubscription(CommentList)`: ```js function withSubscription(WrappedComponent) { @@ -316,17 +316,17 @@ function getDisplayName(WrappedComponent) { ### Не используйте HOC внутри рендер метода {#dont-use-hocs-inside-the-render-method} -Алгоритм сравнения React (или согласование (reconciliation)) использует тождественность компонентов чтобы определить нужно ли обновить существующее под-дерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать под-дерево. Если компоненты не равны, React полностью удалит и заменит старое под-дерево. +Алгоритм сравнения React (или согласование (reconciliation)) использует тождественность компонентов чтобы определить нужно ли обновить существующее поддерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать поддерево. Если компоненты не равны, React полностью удалит и заменит старое поддерево. Normally, you shouldn't need to think about this. But it matters for HOCs because it means you can't apply a HOC to a component within the render method of a component: Обычно нас это не беспокоит. Однако, важно учитывать что мы не можем применять компоненты высшего порядка внутри рендер метода компонента: ```js render() { - // Мы создаем новую версию EnhancedComponent при каждом рендере + // Мы создаём новую версию EnhancedComponent при каждом рендере // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); - // Мы каждый раз размонтируем и монтируем целиком все под-дерево! + // Мы каждый раз размонтируем и монтируем целиком всё поддерево! return ; } ``` @@ -339,7 +339,7 @@ render() { ### Копируйте статические методы {#static-methods-must-be-copied-over} -Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данных GraphQL. +Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данных GraphQL. Когда мы применяем HOC, мы заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статичных методов оригинального компонента. @@ -391,6 +391,6 @@ import MyComponent, { someFunction } from './MyComponent.js'; ### Рефы не передаются {#refs-arent-passed-through} -По конвенции, компоненты высшего порядка передают оборачеваемому компоненту все пропсы, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерахрии контейнера, а не на оборачиваемый компонент. +По конвенции, компоненты высшего порядка передают оборачеваемому компоненту все пропсы, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерархии контейнера, а не на оборачиваемый компонент. Вы можете решить эту проблему с помощью `React.forwardRef` API (представлен в React 16.3). [Узнать больше в главе Пересылка рефов](/docs/forwarding-refs.html). From 8dbb09ef96deb43a9eb4853ee10bab9fd83303ac Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Tue, 19 Feb 2019 18:21:04 -0800 Subject: [PATCH 6/9] Apply suggestions from code review by @lex111 Co-Authored-By: agoldis --- content/docs/higher-order-components.md | 46 ++++++++++++------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 362c19997..16857b1a3 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -14,7 +14,8 @@ const EnhancedComponent = higherOrderComponent(WrappedComponent); Если обычный компонент преобразует пропсы в UI, то компонент высшего порядка преобразует компонент в другой компонент. -HOC часто встречаются в сторонних библиотеках, например [`connect`](https://github.com/reactjs/react-redux/blob/master/docs/api/connect.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options) в Redux и [`createFragmentContainer`](http://facebook.github.io/relay/docs/en/fragment-container.html) в Relay. +HOC часто встречаются в сторонних библиотеках, например [`connect`](https://github.com/reduxjs/react-redux/blob/master/docs/api/connect.md#connect) в Redux и [`createFragmentContainer`](http://facebook.github.io/relay/docs/en/fragment-container.html) в Relay. + В этой главе мы обсудим чем полезны компоненты высшего порядка и как их создавать. @@ -100,10 +101,10 @@ class BlogPost extends React.Component { } ``` -Разница между`CommentList` и `BlogPost` в том, что они вызывают разные методы `DataSource` и рендерят разный вывод, но есть и много общего: +Разница между `CommentList` и `BlogPost` в том, что они вызывают разные методы `DataSource` и рендерят разный вывод. Однако в большинстве своём они похожи: - Оба компонента подписываются на оповещения от `DataSource` при монтировании. -- Оба меняют локальное состояние когда меняется `DataSource`. +- Оба меняют внутреннее состояние при изменении `DataSource`. - Оба отписываются от `DataSource` при размонтировании. Можете представить, что в больших приложениях связка «подписаться на `DataSource`, затем вызвать `setState`» повторяется очень часто. Было бы здорово абстрагировать эту функциональность и использовать ее в других компонентах. @@ -214,11 +215,11 @@ function logProps(WrappedComponent) { Возможно, вы уже заметили сходство между HOC и **компонентами-контейнерами**. Напомним, что при помощи контейнеров мы обычно разделяем общую функциональность от частной. Например, в контейнере мы будем управлять внутренним состоянием или подпиской на внешние ресурсы, и через пропсы передавать данные в компоненты, ответственные за рендер UI. При реализации HOС мы тоже используем контейнеры. Можно сказать что HOC -- это инструмент для параметризированного создания контейнеров. -## Конвенция: передавайте посторонние пропсы оборачиваемому компоненту{#convention-pass-unrelated-props-through-to-the-wrapped-component} +## Соглашение: передавайте посторонние пропсы оборачиваемому компоненту {#convention-pass-unrelated-props-through-to-the-wrapped-component} HOC добавляют компонентам функциональность, но они не должны менять их оригинальное предназначение. Ожидается, что интерфейс компонента, который вы возвращаете из HOC, будет похож на интерфейс оборачиваемого компонента. -Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер метод большинства HOC похож на следующий: +Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер-метод большинства HOC похож на следующий: ```js render() { @@ -238,9 +239,9 @@ render() { } ``` -Эта конвенция помогает нам создавать гибкие и многоразовые компоненты. +Такое соглашение помогает создавать гибкие и многоразовые компоненты. -## Конвенция: Максимизируем композитивность {#convention-maximizing-composability} +## Соглашение: Максимизируем композитивность {#convention-maximizing-composability} Не все HOC выглядят одинаково. Некоторые принимают всего лишь один аргумент -- оборачиваемый компонент: @@ -267,7 +268,7 @@ const ConnectedComment = connect(commentSelector, commentActions)(CommentList); // Вызов функции connect возвращает другую функцию const enhance = connect(commentListSelector, commentListActions); -// Эта функция и есть HOC. Он возвращает компонент, подключенный к хранилищу Redux +// Эта функция и есть HOC. Она возвращает компонент, подключенный к хранилищу Redux const ConnectedComment = enhance(CommentList); ``` Другими словами, `connect` -- это функция высшего порядка, которая возвращает компонент высшего порядка! @@ -292,11 +293,11 @@ const EnhancedComponent = enhance(WrappedComponent) Вы можете найти вспомогательную функцию `compose` во многих сторонних библиотеках, включая lodash (под названием [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](http://redux.js.org/docs/api/compose.html), и [Ramda](http://ramdajs.com/docs/#compose). -## Конвенция: добавьте имя отображения для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} +## Соглашение: добавьте имя отображения для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} Созданные HOC компоненты-контейнеры отображаются в консоли [инструментов разработки React](https://github.com/facebook/react-devtools) наряду с другими компонентами. Для более лёгкой отладки вы можете задать имя, которое подскажет, что определенный компонент был создан с помощью HOC. -Самый распространённый способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, задайте имя отображения `WithSubscription(CommentList)`: +Самый распространённый способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, укажите имя отображения как `WithSubscription(CommentList)`: ```js function withSubscription(WrappedComponent) { @@ -312,14 +313,13 @@ function getDisplayName(WrappedComponent) { ## Предостережения {#caveats} -Вы можете столкнуться с неочевидными проблемами когда работаете с компонентами высшего порядка. +Вы можете столкнуться с неочевидными проблемами, когда работаете с компонентами высшего порядка. -### Не используйте HOC внутри рендер метода {#dont-use-hocs-inside-the-render-method} +### Не используйте HOC внутри рендер-метода {#dont-use-hocs-inside-the-render-method} Алгоритм сравнения React (или согласование (reconciliation)) использует тождественность компонентов чтобы определить нужно ли обновить существующее поддерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать поддерево. Если компоненты не равны, React полностью удалит и заменит старое поддерево. -Normally, you shouldn't need to think about this. But it matters for HOCs because it means you can't apply a HOC to a component within the render method of a component: -Обычно нас это не беспокоит. Однако, важно учитывать что мы не можем применять компоненты высшего порядка внутри рендер метода компонента: +Обычно нас это не беспокоит. Однако, важно учитывать что мы не можем применять компоненты высшего порядка внутри рендер-метода компонента: ```js render() { @@ -335,21 +335,21 @@ render() { Не применяйте HOC в определении другого компонента. Сначала нужно отдельно получить компонент из HOC, и только потом использовать его. Таким образом React будет сравнивать один и тот же компонент при повторном рендере. -При необходимости, в редких случаях, можно динамически применять HOC в методах жизненного цикла или в конструкторе компонента. +При необходимости (в редких случаях) можно динамически применять HOC в методах жизненного цикла или конструкторе компонента. ### Копируйте статические методы {#static-methods-must-be-copied-over} -Иногда бывает полезно определить статические методы компонента. Например, статичный метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данных GraphQL. +Иногда бывает полезно определить статические методы компонента. Например, статический метод `getFragment` библиотеки Relay позволяет составить композицию из фрагментов данных GraphQL. -Когда мы применяем HOC, мы заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статичных методов оригинального компонента. +Когда мы применяем HOC, то заворачиваем оригинальный компонент в контейнер. Поэтому у нового компонента не будет статических методов оригинального компонента. ```js -// Определим статичный метод +// Определим статический метод WrappedComponent.staticMethod = function() {/*...*/} // Теперь применим HOC const EnhancedComponent = enhance(WrappedComponent); -// У расширенного компонента нет статичных методов +// У расширенного компонента нет статических методов typeof EnhancedComponent.staticMethod === 'undefined' // true ``` @@ -364,7 +364,7 @@ function enhance(WrappedComponent) { } ``` -К сожалению, вы должны точно знать какие методы копировать. Вы можете воспользоваться [hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics), чтобы автоматически скопировать не связанные с React статичные методы: +К сожалению, вы должны точно знать какие методы копировать. Вы можете воспользоваться [hoist-non-react-statics](https://github.com/mridgway/hoist-non-react-statics), чтобы автоматически скопировать не связанные с React статические методы: ```js import hoistNonReactStatic from 'hoist-non-react-statics'; @@ -375,7 +375,7 @@ function enhance(WrappedComponent) { } ``` -Другое возможное решение -- экспортировать статичные методы отдельно от компонента. +Другое возможное решение -- экспортировать статические методы отдельно от компонента. ```js // Вместо... @@ -391,6 +391,6 @@ import MyComponent, { someFunction } from './MyComponent.js'; ### Рефы не передаются {#refs-arent-passed-through} -По конвенции, компоненты высшего порядка передают оборачеваемому компоненту все пропсы, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и по-другому обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерархии контейнера, а не на оборачиваемый компонент. +По соглашению компоненты высшего порядка передают оборачеваемому компоненту все пропсы, кроме рефов. `ref` на самом деле не проп, как, например, `key`, и поэтому иначе обрабатывается React. Реф элемента, созданного компонентом из HOC, будет указывать на экземпляр ближайшего в иерархии контейнера, а не на оборачиваемый компонент. -Вы можете решить эту проблему с помощью `React.forwardRef` API (представлен в React 16.3). [Узнать больше в главе Пересылка рефов](/docs/forwarding-refs.html). +Вы можете решить эту проблему с помощью API-метода `React.forwardRef` (добавлен в React 16.3). [Узнать подробнее в главе Перенаправление рефов](/docs/forwarding-refs.html). From 9a4f20ab806f364895e2e66f5343d0200cbd80c5 Mon Sep 17 00:00:00 2001 From: ANOTHER GUY Date: Thu, 21 Feb 2019 15:50:44 -0800 Subject: [PATCH 7/9] Apply suggestions from code review by @another-guy Co-Authored-By: agoldis --- content/docs/higher-order-components.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 332ff12d4..37cc08cb8 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -163,9 +163,9 @@ function withSubscription(WrappedComponent, selectData) { } ``` -Заметьте, что HOC ничего не меняет и не наследует поведение оборачиваемого компонента, вместо этого HOC *оборачивает* оригинальный компонент в контейнер посредством *композиции*. HOC является чистой функцией без посторонних эффектов. +Заметьте, что HOC ничего не меняет и не наследует поведение оборачиваемого компонента, вместо этого HOC *оборачивает* оригинальный компонент в контейнер посредством *композиции*. HOC является чистой функцией без побочных эффектов. -Вот и всё! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп `data`. Для HOC не важно как будут использоваться данные, а оборачиваемому компоненту не важно откуда они берутся. +Вот и всё! Оборачиваемый компонент получает все пропсы, переданные контейнеру, а также проп `data`. Для HOC не важно, как будут использоваться данные, а оборачиваемому компоненту не важно, откуда они берутся. Так как `withSubscription` -- это обычная функция, то мы можем убрать или добавить любое количество аргументов. Например, мы могли бы сделать конфигурируемым название пропа `data` и ещё больше изолировать HOC от оборачиваемого компонента. Также мы можем добавить аргумент для конфигурации `shouldComponentUpdate` или источника данных. Всё это возможно, потому что HOC полностью контролирует процесс создания компонента. @@ -189,7 +189,7 @@ function logProps(InputComponent) { const EnhancedComponent = logProps(InputComponent); ``` -В приведённом выше примере мы не можем повторно использовать `InputComponent` отдельно от `EnhancedComponent`. Важнее то, что если мы захотим обернуть `EnhancedComponent` в другой HOC, который *тоже* меняет `componentWillReceiveProps`, то мы сотрем функциональность заданную первым HOC! Более того, `EnhancedComponent` не работает с функциональными компонентами потому, что у них отсутствуют методы жизненного цикла. +В приведённом выше примере мы не можем повторно использовать `InputComponent` отдельно от `EnhancedComponent`. Важнее то, что если мы захотим обернуть `EnhancedComponent` в другой HOC, который *тоже* меняет `componentWillReceiveProps`, то мы сотрём функциональность заданную первым HOC! Более того, `EnhancedComponent` не работает с функциональными компонентами, потому что у них отсутствуют методы жизненного цикла. Мутирующие HOC являются хрупкой абстракцией, они конфликтуют с другими HOC, мы не сможем просто применять их без того, чтобы знать что именно они меняют. @@ -218,7 +218,7 @@ function logProps(WrappedComponent) { HOC добавляют компонентам функциональность, но они не должны менять их оригинальное предназначение. Ожидается, что интерфейс компонента, который вы возвращаете из HOC, будет похож на интерфейс оборачиваемого компонента. -Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться оборачиваемогу компоненту. Рендер-метод большинства HOC похож на следующий: +Пропсы, которые напрямую не связаны с функциональностью HOC, должны передаваться без изменений оборачиваемогу компоненту. Рендер-метод большинства HOC похож на следующий: ```js render() { @@ -238,7 +238,7 @@ render() { } ``` -Такое соглашение помогает создавать гибкие и многоразовые компоненты. +Такое соглашение помогает создавать гибкие повторно используемые компоненты. ## Соглашение: Максимизируем композитивность {#convention-maximizing-composability} @@ -288,7 +288,7 @@ const enhance = compose( const EnhancedComponent = enhance(WrappedComponent) ``` -(Поэтому мы можем использовать `connect`, и другие расширяющие функциональность HOC, в качестве экспериментальных JavaScript декораторов.) +(Поэтому мы можем использовать `connect` и другие расширяющие функциональность HOC в качестве экспериментальных JavaScript декораторов.) Вы можете найти вспомогательную функцию `compose` во многих сторонних библиотеках, включая lodash (под названием [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](http://redux.js.org/docs/api/compose.html), и [Ramda](http://ramdajs.com/docs/#compose). @@ -316,7 +316,7 @@ function getDisplayName(WrappedComponent) { ### Не используйте HOC внутри рендер-метода {#dont-use-hocs-inside-the-render-method} -Алгоритм сравнения React (или согласование (reconciliation)) использует тождественность компонентов чтобы определить нужно ли обновить существующее поддерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать поддерево. Если компоненты не равны, React полностью удалит и заменит старое поддерево. +Алгоритм сравнения React (известный как согласование или reconciliation) использует тождественность компонентов чтобы определить нужно ли обновить существующее поддерево, или убрать и монтировать вместо него новое. Если компонент, полученный из `render`, идентичен (`===`) компоненту из предыдущего рендера, то React рекурсивно продолжит сравнивать поддерево. Если компоненты не равны, React полностью удалит и заменит старое поддерево. Обычно нас это не беспокоит. Однако, важно учитывать что мы не можем применять компоненты высшего порядка внутри рендер-метода компонента: From 24b0bd2ffca0e0808b24fa8d50caf1c41b09c811 Mon Sep 17 00:00:00 2001 From: ANOTHER GUY Date: Thu, 21 Feb 2019 16:00:10 -0800 Subject: [PATCH 8/9] Update mixin term Co-Authored-By: agoldis --- content/docs/higher-order-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 37cc08cb8..2b66c9d40 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -22,7 +22,7 @@ HOC часто встречаются в сторонних библиотека > **Примечание** > -> В прошлом мы рекомендовали миксины для реализации сквозной функциональности, но со временем выяснилось, что от них больше вреда, чем пользы. [Узнайте](/blog/2016/07/13/mixins-considered-harmful.html), почему мы решили убрать миксины и как переписать старые компоненты. +> В прошлом мы рекомендовали примеси для реализации сквозной функциональности, но со временем выяснилось, что от них больше вреда, чем пользы. [Узнайте](/blog/2016/07/13/mixins-considered-harmful.html), почему мы решили убрать примеси и как переписать старые компоненты. Традиционные компоненты подразумевают многократное использование, но не позволяют с лёгкостью решить некоторые проблемы. From 6323e5771c916aa7368ce3c5822e4f7da51798fa Mon Sep 17 00:00:00 2001 From: Andrew Goldis Date: Fri, 22 Feb 2019 17:58:06 +1100 Subject: [PATCH 9/9] Use proper "display name" term --- TRANSLATION.md | 2 +- content/docs/higher-order-components.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/TRANSLATION.md b/TRANSLATION.md index d0c4981e5..be2e93cc1 100644 --- a/TRANSLATION.md +++ b/TRANSLATION.md @@ -193,7 +193,7 @@ React elements are [immutable](https://en.wikipedia.org/wiki/Immutable_object). | development | разработка | | development mode | режим разработки | | developer tools | инструменты разработки | -| display name | имя отображения | +| display name | отображаемое имя | | DOM container | DOM-контейнер | | effect | эффект | | error | ошибка | diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md index 2b66c9d40..63068116b 100644 --- a/content/docs/higher-order-components.md +++ b/content/docs/higher-order-components.md @@ -292,11 +292,11 @@ const EnhancedComponent = enhance(WrappedComponent) Вы можете найти вспомогательную функцию `compose` во многих сторонних библиотеках, включая lodash (под названием [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](http://redux.js.org/docs/api/compose.html), и [Ramda](http://ramdajs.com/docs/#compose). -## Соглашение: добавьте имя отображения для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} +## Соглашение: добавьте отображаемое имя для легкой отладки {#convention-wrap-the-display-name-for-easy-debugging} Созданные HOC компоненты-контейнеры отображаются в консоли [инструментов разработки React](https://github.com/facebook/react-devtools) наряду с другими компонентами. Для более лёгкой отладки вы можете задать имя, которое подскажет, что определенный компонент был создан с помощью HOC. -Самый распространённый способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, укажите имя отображения как `WithSubscription(CommentList)`: +Самый распространённый способ -- это обернуть имя оборачиваемого компонента. Например, если вы назвали компонент высшего порядка `withSubscription`, а имя оборачиваемого компонента было `CommentList`, то отображаемое имя будет `WithSubscription(CommentList)`: ```js function withSubscription(WrappedComponent) {