Как да се справим с контекста на React по надежден начин.

Контекст - не е предназначен каламбур -

Еднопосочният поток от данни е една от най-завладяващите черти на React. Това прави приложението ви по-лесно да разсъждавате. Можете да разберете какви точно реквизити се предават, само като погледнете вашия компонент. Можете дори да получите повече от него, като приемете модела на контейнер срещу презентационен компонент.

Но ако сте използвали React от известно време, има вероятност да сте намерили ситуация, в която искате да предавате данни през компонентното дърво, без да се налага да предавате подпорите на всяко ниво.

React има функция за решаване на този проблем: контекст.

Вероятно печелите от това дори без да го осъзнавате. Благодарение на някои от най-използваните библиотеки в екосистемата React се възползват от нея: React-Router, React-Redux и много други.

Но, идва с цена и трябва да внимавате. Документацията на React ни предупреждава за това:

https://facebook.github.io/react/docs/context.html

Тази публикация има за цел да ви покаже как да използвате контекста React надеждно. Очертаване на най-често срещаните клопки и как да ги избегнем.

Как работи контекстът на React?

Първо се нуждаете от компонент на доставчика на контекст. Което не е нещо повече от нормален компонент, който изисква childContextTypes и getChildContext:

  • childContextTypes: статично свойство, което ви позволява да декларирате структурата на контекстния обект, предавана на потомците на вашия компонент. Подобна мода на propTypes, но не е задължителна.
  • getChildContext: метод на прототип, който връща контекстния обект, за да предаде надолу по йерархията на компонента. Всеки път, когато състоянието се промени или компонентът получи нови реквизити, този метод ще бъде извикан.

Нека да видим как се прави:

клас ContextProvider разширява React.Component {
  статични childContextTypes = {
    currentUser: React.PropTypes.object
  };
  getChildContext () {
    връщане {currentUser: this.props.currentUser};
  }
  
  render () {
    се върне (...);
  }
}

Всеки потомък на този компонент може да получи достъп до контекста, както следва:

/ * Компонент от клас ES6 * /
клас ContextConsumer разширява React.Component {
  / *
     contexTypes е задължително статично свойство за деклариране
     какво искате от контекста
  * /
  статичен контекстTypes = {
      currentUser: React.PropTypes.object
  };
  render () {
    const {currentUser} = this.context;
    връщане 
{currentUser.name}
;   } }
/ * Компонент от клас ES6 с презаписан конструктор * /
клас ContextConsumer разширява React.Component {
  статичен контекстTypes = {
      currentUser: React.PropTypes.object
  };
  конструктор (реквизит, контекст) {
    супер (реквизит, контекст);
    ...
  }
  render () {
    const {currentUser} = this.context;
    връщане 
{currentUser.name}
;   } }
/ * Функционален компонент без състояние * /
const ContextConsumer = (реквизит, контекст) => (
  
{context.currentUser.name} ); ContextConsumer.contextTypes = {   currentUser: React.PropTypes.object };

Потребителските компоненти на контекста трябва да декларират това, което искат от контекста, чрез contextTypes. Тогава те могат да получат достъп до контекста чрез this.context в случая на компонентите от клас ES6. Вместо това, когато вашият потребител е функционален компонент без състояние, можете да получите достъп до контекста чрез втория параметър на функцията.

Освен това можете да посочите контекста в някои от методите на жизнения цикъл на компонента:

компонентWillReceiveProps (nextProps, nextContext) {...}
shouldComponentUpdate (nextProps, nextState, nextContext) {...}
компонентWillUpdate (nextProps, nextState, nextContext) {...}
компонентDidUpdate (предишенProps, предишенContext) {...}

Кои са недостатъците?

  1. Както споменахме по-горе, React контекстът е експериментален, API вероятно ще се промени в бъдеще. Това прави кода, който го използва, прекалено крехък. Всяка промяна в API ще наруши всеки контекст доставчик и потребител. Водещ до кошмар за поддръжка.
  2. Също така контекстът React е основно глобална променлива в обхвата на едно подребрие React. Това прави вашите компоненти по-свързани и почти невъзможно да се използват отново извън гореспоменатото поддърво.
  3. Освен това, ако контекстна стойност, предоставена от компонент, се промени, потомците, които използват тази стойност, няма да се актуализират, ако междинен родител върне фалшиво от trebaComponentUpdate:

Woah! Трябва да се вземе предвид много ... и възниква въпрос:

Трябва ли да използвам React нестабилна контекстна функция?

Дан Абрамов го закова!

Нека да видим как с помощта на компоненти от по-висок ред (HOCs за краткост) можете да смекчите някои от присъщите проблеми с използването на контекста React.

HOCs на помощ

HOC е функция, която приема базов компонент React и връща нов компонент с допълнителна функционалност. По принцип HOC подобрява компонент с нови възможности. По този начин можете да абстрахирате общото поведение на парчета за многократна употреба.

Повече за HOCs:
Миксините са мъртви. Състав на дълго живо
Компоненти с по-висока поръчка: модел на дизайн на реактивно приложение
Миксините се считат за вредни

Предоставяйки HOCs, можете лесно да разрешите проблема с нестабилността на кода в контекста React. Ще трябва да актуализирате само едно място, ако API се промени. Нека илюстрираме възможно изпълнение:

const provideContext =
  (childContextTypes, getChildContext) => (Компонент) => {
    клас ContextProvider разширява React.Component {
      статични childContextTypes = childContextTypes;
      getChildContext = () => getChildContext (this.props);
        
      render () {
        връщане <Компонент {... this.props} />;
      }
    }
    връщане ContextProvider;
  };
const consumeContext = (contextTypes) => (Компонент) => {
  / * Контекстът се предава като реквизит. По този начин компонентът е
   напълно отделен от контекстния API.
  * /
  const ContextConsumer = (реквизит, контекст) =>
    <Компонент {... реквизит} {... контекст} />;
  ContextConsumer.contextTypes = contextTypes;
  връщане ContextConsumer;
};

След това можете да го използвате, както следва:

const Child = ({color}) => (
  
    Здравейте контекст !!!    ); const ChildwithContext = consumeContext ({   цвят: React.PropTypes.string }) (Child);
const MiddleComponent = () => ;
const App = provideContext (
  {color: React.PropTypes.string},
  () => ({color: 'red'})
) (MiddleComponent);

Друга алтернатива е да използвате невероятната библиотека Препоръчайте:

Препоръчайте предоставя куп много полезни HOC, които да отговарят на всички ваши нужди. Ако се прегърнете Препоръчайте, ще промените начина, по който пишете Реагирайте приложения. Той предоставя withContext и getContext като алтернативи съответно на ourContext и consumeContext.

Като предимство вече не е нужно да поддържате HOCs. Но ако приложението ви е малко или имате нужда само от тези две функции, разчитането на Препоръчай може да е излишно.

Не използвайте контекст, за да предавате данните на модела си през компоненти

НОК ви дават много, но не достатъчно ... От вас зависи да изберете какви данни да предадете през контекста.

Ако предадете данни за модела, ще получите само още проблеми:

  1. Прави вашето приложение по-трудно да разсъждавате.
  2. Двойки вашите компоненти.

Използвайте контекст за истински глобали, а не само за записване на запис. Настоящият влязъл потребител, информация за темата, интернационализация и локализация са добри примери.

Работен пример, моля!

Заключения

При разработването на софтуер, като в живота като цяло, всичко е свързано с компромиси. И ... макар и нестабилен, React контекстът е мощен инструмент на ваше разположение. Но, знаете старата поговорка: с голяма сила идва голяма отговорност.

Така че, използвайте контекста по надежден начин. ХОС не са панацея, но помагат много по тези въпроси.

Надявам се този пост да ви даде по-добра перспектива и да изясни малко функцията на контекста React. Ако е така, моля препоръчайте го, за да достигне до повече хора.

Огромни благодарности към Дан Абрамов за прегледа на този пост. Наистина оценявам приноса му към общността.

Може да се интересувате също от:

  • Как да се справим със състоянието в React. Липсващите често задавани въпроси.
  • Жизненият цикъл на реагиращия компонент
  • Компоненти, бойните коне на React.
  • Реагирайте, леко въведение.