diff --git a/content/docs/higher-order-components.md b/content/docs/higher-order-components.md
index a7a123abe..216347d9f 100644
--- a/content/docs/higher-order-components.md
+++ b/content/docs/higher-order-components.md
@@ -1,32 +1,32 @@
---
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.
+Компонент вищого порядку (КВП) — це просунута технологія для повторного використання логіки компоненту. Сам по собі КВП не є частиною React API, але через композиційну природу компонентів він є розповсюдженним патерном проектування.
-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/reduxjs/react-redux/blob/master/docs/api/connect.md#connect) and Relay's [`createFragmentContainer`](http://facebook.github.io/relay/docs/en/fragment-container.html).
+КВП поширені у таких сторонніх бібліотеках, як [`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.
-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}
+## Використання КВП для перехресної функціональності {#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.
+Зазвичай компоненти є основною одиницею повторного використання коду у React. Однак ви побачите, що вони не є відповідним рішенням низки проблем.
-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 +34,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 +68,7 @@ class CommentList extends React.Component {
}
```
-Later, you write a component for subscribing to a single blog post, which follows a similar pattern:
+Пізніше, ви створите компонент `BlogPost` для підписки на одну публікацію блогу, який реалізується за схожою логікою:
```js
class BlogPost extends React.Component {
@@ -100,15 +100,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`.
+- Викликають `setState`, коли у джерелі даних відбуваються зміни.
+- При демонтуванні вони відписуються від `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.
+Чи можете ви уявити, що у великому додатку ця схема підписки буде виникати знову і знову? Нам потрібна абстракція, яка дозволить описати цю логіку в одному місці та звертатися до неї у багатьох компонентах. Саме тут доречно використати компонент вищого порядку.
-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`:
+Ми можемо написати функцію, що створює компоненти, як `CommentList` чи `BlogPost`, які підписуються на `DataSource`. В якості одного з аргументів функція буде приймати дочірній компонент, який отримає дані з підписки у якості власного prop. Назвемо цю функцію `withSubscription`:
```js
const CommentListWithSubscription = withSubscription(
@@ -122,14 +122,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` буде переданий параметр `data` у якості пропа з актуальними даними, отриманими від `DataSource`:
```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 +140,7 @@ function withSubscription(WrappedComponent, selectData) {
}
componentDidMount() {
- // ... that takes care of the subscription...
+ // ... тут відбувається підписка ...
DataSource.addChangeListener(this.handleChange);
}
@@ -155,25 +155,25 @@ 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.
+Зверніть увагу на те, що КВП не модифікує отриманий компонент та не наслідує його поведінку. Швидше, КВП створює *композицію*, *обгортаючи* оригінальний компонент у контейнер. КВП є чистою функцією без побічних ефектів.
-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.
+От і все! Обгорнутий компонент отримує всі пропси, що були передані до контейнера, разом з новим prop — `data`, який він використовує для відображення UI. Для компонента вищого порядку не має значення, як саме дані будуть використані, а для обгорнутого компонента не має значення, звідки вони з'явились.
-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` конфігуруємим, щоб додатково ізолювати КВП від обгорнутого компонента. Або ви можете прийняти аргумент, що налаштує `shouldComponentUpdate` чи джерело даних. Це все можливо тому, що КВП має повний контроль над тим, як компонент буде визначений.
-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` і обгорнутим компонентом здійснюється лише за допомогою пропсів. Це дозволяє легко замінити один КВП на інший, якщо вони забезпечують однакові пропси для обгорнутого компонента. Це може бути корисним, наприклад, якщо ви зміните бібліотеки для отримання даних.
-## 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.
+Втримайтеся від бажання змінити прототип компонента (чи мутувати його) всередині КВП.
```js
function logProps(InputComponent) {
@@ -181,20 +181,20 @@ function logProps(InputComponent) {
console.log('Current props: ', this.props);
console.log('Next props: ', 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`, який, наприклад, у свою чергу мутує `componentWillReceiveProps`, функціональність першого КВП буде перезаписана! Цей КВП також не буде працювати з функціональними компонентами, які не мають методів життєвого циклу.
-Mutating HOCs are a leaky abstraction—the consumer must know how they are implemented in order to avoid conflicts with other HOCs.
+Мутуючий КВП є крихкою абстракцією — споживач повинен знати, як вони реалізуються, щоб уникнути конфліктів з іншими КВП.
-Instead of mutation, HOCs should use composition, by wrapping the input component in a container component:
+Замість мутації, КВП мають реалізовувати композицію, обгортаючи переданий компонент у контейнер:
```js
function logProps(WrappedComponent) {
@@ -204,34 +204,34 @@ function logProps(WrappedComponent) {
console.log('Next props: ', 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.
+Цей КВП має таку ж функціональність, як і мутована версія, уникаючи при цьому можливих проблем. Він однаково добре працює з класовими та функціональними компонентами. А тому, що це чиста функція, вона може бути поєднана з іншим КВП, або навіть сама з собою.
-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.
+Можливо, ви вже помітили схожість між КВП та патерном, що називається компонент-контейнер. Нагадаємо, що **компонент-контейнер** є частиною політики відокремлення відповідальності між високорівневими та низькорівневими задачами. Контейнери керують такими речами, як підписка на зовнішні ресурси та внутрішній стан, та передають пропси у компоненти, що у свою чергу відповідають за відображення UI. КВП використовують контейнери, як частину власної реалізації. Ви можете вважати КВП параметризованим визначенням компонента-контейнера.
-## 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.
+КВП додає компоненту функціональність. Він не повинен змінювати його початкове призначення. Очікується, що повернений КВП компонент буде мати інтерфейс аналогічний обгорнутому.
-HOCs should pass through props that are unrelated to its specific concern. Most HOCs contain a render method that looks something like this:
+КВП повинні передавати ті пропси, що не пов'язані з їх функціональністю, у незмінному стані. Більшість компонентів вищого порядку мають рендер-метод, схожий на цей:
```js
render() {
- // Filter out extra props that are specific to this HOC and shouldn't be
- // passed through
+ // Відфільтруйте зайві пропси, що характерні для КВП та не мають
+ // потрапити до компонента
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` - це функція вищого порядку, яка повертає компонент вищого порядку!
+
+Ця форма може здатися заплутаною або непотрібною, але вона має корисну властивість. Одноаргументні КВП, як і той, який повертається функцією `connect`, мають сигнатуру `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
+ // Обидва параметра є КВП та приймають лише один аргумент
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` та інші поширюючи функціональність КВП у якості експериментальних JavaScript декораторів.)
-The `compose` utility function is provided by many third-party libraries including lodash (as [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](https://redux.js.org/api/compose), and [Ramda](https://ramdajs.com/docs/#compose).
+Ви можете знайти допоміжну функцію `compose` у багатьох сторонніх бібліотеках, включаючи lodash (під назвою [`lodash.flowRight`](https://lodash.com/docs/#flowRight)), [Redux](https://redux.js.org/api/compose) та [Ramda](https://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.
+Створений КВП компонент-контейнер відображається у [React Developer Tools](https://github.com/facebook/react-devtools), як і будь-який інший компонент. Для того, щоб полегшити процес налагодження, визначте відображуване ім’я, яке повідомляє, що це результат КВП.
-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) {
@@ -314,60 +314,60 @@ 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.
+Якщо ви лише починаєте використовувати React, то варто зазначити, що компоненти вищого порядку можуть стати причиною неочевидних проблем.
-### Don't Use HOCs Inside the render Method {#dont-use-hocs-inside-the-render-method}
+### Не використовуйте КВП у середині рендер-методів {#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 (або узгодження), використовує перевірку на тотожність компонента, щоб визначити, чи слід оновити існуюче піддерево компонентів або слід знищити його та змонтувати нове. Якщо компонент, що був повернений рендер-методом, ідентичний (`===`) попередньому результату, 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.
+Замість цього застосовуйте КВП за межами визначення компонента, щоб отриманий компонент був створений лише один раз. У цьому випадку 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.
+У тих рідкісних випадках, коли вам потрібно динамічно застосовувати КВП, ви можете зробити це в методах життєвого циклу компонента або в його конструкторі.
-### 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.
+Якщо ви застосовуєте КВП, то обгортаєте оригінальний компонент контейнером. Це означає, що новий компонент не матиме статичних методів оригінального компонента.
```js
-// Define a static method
+// Визначте статичний метод
WrappedComponent.staticMethod = function() {/*...*/}
-// Now apply a HOC
+// Тепер застосуйте КВП
const EnhancedComponent = enhance(WrappedComponent);
-// The enhanced component has no static method
+// EnhancedComponent не має статичного методу
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):
```js
import hoistNonReactStatic from 'hoist-non-react-statics';
@@ -378,22 +378,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. Якщо ви додасте `ref` до компонента, що був повернений КВП, він буде вказувати на екземпляр зовнішнього контейнера, а не на обгорнутий компонент.
-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).
+Розв’язання цієї проблеми є використання API `React.forwardRef` (введений з React 16.3). [Дізнайтеся більше про це в розділі Переадресація рефів](/docs/forwarding-refs.html).