Одной из наиболее важных концепций реагирования является однонаправленным потоком данных. Данные вводят в систему в одной точке и потоки вниз по течению с каждым компонентным фильтром и дополняют эти данные для своих детей. Это невероятно простая, но также очень мощная парадигма, которая позволяет строить сложные системы с простыми потоками данных.
При попытке создать обобщенные компоненты, которые могут быть составлены в интересные, и часто непредвиденные способы, я узнал, что передача данных через реквизиты не обязательно было лучшее решение. Это было наиболее очевидно при проектировании компонентов «контейнера», которые необходимы для реагирования (на каламке) для изменений вниз по течению.
Пример времени
Давайте возьмем Scroller на основе JS, который необходимо настроить его размер прокрутки, чтобы отразить размер содержимого. (Конечно, вы можете использовать нативные скроллеры с поведением прокрутки: гладкие, прослушиватели пассивных событий и пересечений, но мы предполагаем, что наша целевая платформа не поддерживает тех, кто не поддерживает тех или у нас есть некоторые другие требования к ZANY). Он может начать сначала сделать с контентом в два раза длиннее, чтобы прокрутка половина высоты.
Давайте введем компонент аккордеона в нашем Scroller. Как мы обращаемся к компонентам, которые изменяются в размере? Типичный подход может быть добавлен обработчик событий, который будет называться, когда аккордеон открывается или закрывается, а затем сообщите Scloller обновить.
class ScrollerSample extends React.Component { handleOpenClose = () => { } render () { return (); } } {someChildren}
Но как мы уведомим, что скроллер изменений в размере? Вы можете сделать размер содержимого опоры на прокрутке и пересчитывать его, но это неправильно несет ответственность и добавляет много бойщиков. Визуализация скроллера с несколькими компонентами, которые изменяют размер; Вам придется подключить несколько обработчиков событий для адаптации уведомлений о размерах и переоценить содержимое просто для функционирующего скроллера!
Другой вариант является императивным API для скроллера, с которой вы можете вызвать метод обновления ().
class ScrollerSample extends React.Component { handleOpenClose = () => { // scroller, measure thyself! this.scroller.update(); } setScroller = (scroller) => { this.scroller = scroller; } render () { return (); } } {someChildren}
Это не так уж плохо для одной интеграции, но может начать выходить из контроля, тем больше компоненты размером с переменной размером. Но что, если аккордеон находится внутри другого пользовательского компонента, MyMenu? Вам придется добавить реквизит к этому компоненту, чтобы пройти обратные вызовы до аккордеона. Кроме того, если Scroller и Accordion являются из той же библиотеки компонентов, вы ожидаете их «просто работать» из коробки, верно?
Введите React Context
Не было бы здорово, если бы реагировал способ пройти произвольные данные или ссылки на иерархию компонентов, чтобы обеспечить почти беспроблемную интеграцию между компонентами?
Контекст Rection является то, что магический инструмент (который вы, вероятно, не должны использовать)!.
Но, может быть, у вас есть хороший случай. Я бы утвердовал, что наш пример здесь такой случайный случай, но я мог быть убежден в противном случае. Давайте так предположим и изучите, как контекст может помочь решить проблему.
Как это работает
Помимо реквизитов (данные, отправляемые в компонент из контейнера) и состояния (данные, поддерживаемые компонентом), ложь контекст. Данные, переданные в контексте, проходят вниз по дерево компонента, но доступно только при его компоненте. Подумайте о контексте как брызгает – любой дочерний компонент может иметь их, но только если они спрашивают красиво.
Подумайте о контексте как брызгает – любой дочерний компонент может иметь их, но только если они спрашивают красиво.
Контекст против рек
Параметры контекста очень похожи на реквизиты в том, что они:
- Может быть любым типом данных, включая функции,
- Определяются с использованием типов опоры,
- Зарегистрированы с использованием статического элемента, ContextTypes, на компоненте и
- Поток по иерархии компонента.
Вы определяете реквизит компонент отправляет или получает, используя то же самое означает, что вы можете использовать для реквизитов: объект с ключами, представляющими опоры и значения для их типов. После определения прикрепите объект к статическому элементу, ContextTypes на вашем компоненте и реагирование пройдут данные от последнего предка, который его предоставил. Контекст мусор. Любой компонент в дереве может обеспечить ценность для своих детей для любого ключа, который он выбирает – даже если этот ключ использовался предком!
Вот простой пример компонента, который может получить значение из контекста. Поскольку данные контекста не могут потребоваться, как оперирование может быть для проверки оформления времени, важно, чтобы потребители контекста были устойчивыми как к пустым контексте, так и в неожиданные значения.
import PropTypes from 'prop-types'; import React from 'react'; const MyComponent = (props, context /* Look, a new arg! */) => ({/* Pluck value from context */} {context.value || 'Context was empty :('}); // Tell React which context types MyComponent should receive MyComponent.contextTypes = { value: PropTypes.string };
Хотя приемные данные контекста очень похожи, сторона отправителя немного отличается. Чтобы пройти данные через контекст, отправитель должен:
- Определите те же типы данных, что и приемник на статическом элементе – ChildContextTypes,
- Реализуйте специальный метод жизненного цикла, GetChildContext (), который будет вызван путем реагирования на заполнение контекста для всех потомков.
Подразделение последней точки заключается в том, что контекст может быть предоставлен только «полными» компонентами (те, которые наследуют от React.comPonent или создаются CreateClass), а не функциональными компонентами без гражданства (SFC).
Вот основной пример компонента, который отправляет значение контекста по его компоненту дерева. В этом примере любой компонент, созданный в MyProvider, который также определяет, одинаковые типы контекста получат «мой провайдер» в ключ значений контекста.
import PropTypes from 'prop-types'; import React from 'react'; const MyProvider = class extends React.Component { getChildContext () { return { value: 'From MyProvider' }; } render () { return (); } }; MyProvider.childContextTypes = { value: PropTypes.string };All my children know my name
{children}
Понимание потока данных
Однако ключевая разница с реквизитами является то, как контекст вытекает на иерархию компонентов. Контекст не требует, чтобы вы явно передавали данные через каждый слой дерева; Он протекает вниз по течению от провайдера до тех пор, пока не перезаписан потомком или достигает последнего потомка.
Эта возможность позволяет некоторую полезную магию. Например, я могу безопасно обернуть MyComponent с другим пользовательским компонентом, и он все равно будет работать с MyProvider! Ниже MyComponent все равно будет отображать значение, передаваемое MyProvider, хотя в середине есть другой компонент (это не пересылает никакие реквизиты).
import MyComponent from './MyComponent'; import MyProvider from './MyProvider'; const InTheMiddle = (props) => (This component decorates MyComponent without impeding the data passed via Context.); const App = (props) => ();
Остерегайтесь, там будут драконы!
Хотя контекст – это мощный инструмент, это тот, который не следует использовать слегка. Есть два значимых «гатча» – один архитектурный и один технический – это не следует упускать из виду.
Во-первых, контекст делает ваши приложения более сложными для GROK. В типичном приложении React React Data течет от компонента до компонента через реквизиты. Вы можете очень легко увидеть, как передается данные, что является мощным как при разработке приложения и при поддержке его со временем (особенно когда другие поддерживают его!). Контекстные данные течет за пределы обычного пути и, как таковые, легко пропустить. Когда вы восстанавливаете приложение, что-то может перестать работать, и неясно, почему, поскольку данные, переданные через контекст, были потеряны или неожиданно переписаны.
Во-вторых, изменения в контекстных данных могут быть предотвращены компонентом, который возвращает TRUE от SOCCOMPONENTUPDATE (). Эта «особенность» поймала меня недавно. Если вы проходите данные через контекст, который может измениться, если потомк предотвращает обновление, его потомки будут застрять со стральником контекста – даже если они сами позже обновляют себя. Другими словами, после того, как любой компонент в дереве предотвращает обновления, остальная часть его поддерева будет заблокирована от обновлений до контекстных данных до тех пор, пока каждый компонент между ним и предоставлением не позволяет обновлять.
В результате, если вы решите использовать контекст, я настоятельно рекомендую только передавать неизменные данные или функции в контексте. Лучше пройти Геттер (например, getValue ()), чем примитивное значение, чтобы убедиться, что все потомки всегда могут получить самое последнее значение.
Если вы решите использовать контекст, я настоятельно рекомендую только передавать неизменные данные или функции в контексте.
Надеюсь, что поможет вам понять, какой контекст есть и как использовать (или избежать!) Это. Вы успешно использовали контекст? Есть ли другие ловушки, о которых другие должны знать?