React 16.3-alpha опубликован в npm, его уже можно загружать и использовать. Сегодня мы поговорим о самых крупных и интересных нововведениях этого релиза. В частности, речь пойдёт об API Context, о новых методах жизненного цикла, о статическом методе getDerivedStateFromProps, о компонентах StrictMode и AsyncMode, а также об обновлённых инструментах разработчика React.

image

Новое API Context


API Context всегда выглядело как нечто таинственное. С одной стороны — это официальное, документированное API React, но, с другой стороны, разработчиков предупреждали о том, что использовать его не следует, так как оно может со временем измениться, и документация по нему, намеренно, была неполной. Теперь время этого API настало, фаза RFC пройдена, и новое API, которое, определённо, стало более дружелюбным, доступно разработчикам. Оно может облегчить вам жизнь, если всё, что вам нужно — простое управление состоянием без «излишеств» Redux или MobX.

Новое API доступно в виде React.createContext(), оно даёт в наше распоряжение два компонента:


Создание нового контекста командой React.createContext

Тут, вызвав фабричную функцию, мы получаем объект, в котором имеются элементы Provider и Consumer.

Provider — это особый компонент, цель существования которого заключается в том, чтобы предоставлять данные всем компонентам в собственном поддереве. Например, это может выглядеть так:


Использование нового API Context — Context.Provider

Тут выбрано поддерево (в данном случае — всё дерево), которому мы хотим передать контекст theme, и установлены значения, которые нужно передать. Значения, конечно, могут быть динамическими (то есть, основанными на this.state).

Следующий шаг заключается в использовании элемента Consumer:


Использование нового API Context — Context.Consumer

Если случилось так, что вы рендерите Consumer не внедряя его в соответствующий Provider, будут использованы значения по умолчанию, объявленные в createContext.
Обратите внимание на следующее:

  • У компонента Consumer должен быть доступ к уже используемому компоненту Context. Если будет создан новый контекст, с теми же входными параметрами, это равносильно созданию нового компонента Context, в результате те данные, которые были в исходном компоненте Context, компоненту Consumer переданы не будут. По этой причине к Context стоит относиться как к компоненту — его нужно создавать один раз, а затем экспортировать или импортировать там, где это нужно.
  • В новом синтаксисе используется шаблон React «функция как потомок» (иногда это называют render prop). Если вы с этим шаблоном не знакомы — взгляните на данный материал.
  • При работе с новыми конструкциями, если планируется использовать новое API Context, больше не нужно использовать prop-types для указания contextProps.

Данные из контекста, передаваемые функции, соответствуют свойству value, установленному в провайдерах компонента Context.provider, и изменение данных в компоненте Provider приводит к перерисовке всех потребителей этих данных.

Новые методы жизненного цикла


Ещё одно новшество, которое, из стадии RFC, перешло в рассматриваемый альфа-релиз React, касается признания устаревшими некоторых методов жизненного цикла и введения нескольких новых методов.

Это изменение нацелено на внедрение рекомендованных подходов к разработке (вот материал о том, почему с этими функциями может быть непросто обращаться), что обретёт особую важность после того, как будет полностью активирован асинхронный режим рендеринга (что является одной из важнейших целей архитектуры Fiber, появившейся в React 16).

Вот функции, которые через некоторое время признают устаревшими:

  • Функция componentWillMount, вместо которой предлагается использовать? componentDidMount.
  • Функция componentWillUpdate, которую заменит componentDidUpdate.
  • Функция componentWillReceiveProps?. В качестве заменителя этой функции предлагается новая статическая функция? getDerivedStateFromProps.

В свете вышесказанного не стоит впадать в панику, так как эти функции всё ещё можно использовать. Уведомления о том, что данные функции устарели, появятся в React 16.4, а их удаление запланировано в 17-й версии React.


Паника в стане React-разработчиков. Дэн Абрамов предлагает не беспокоиться, но его слова действуют не на всех

Уведомления будут выводиться только при использовании новых компонентов StrictMode или AsyncMode, но и в этих случаях их можно отключить, используя следующие методы:

  • UNSAFE_componentWillMount
  • UNSAFE_componentWillReceiveProps
  • UNSAFE_componentWillUpdate

Статический метод getDerivedStateFromProps


Так как componentWillReceiveProps собираются убрать, нужны какие-то механизмы для обновления состояния на основании изменения свойств. Для решения этой задачи было решено представить новый статический метод.

Что такое статический метод? Это — метод, который существует в классе, а не в его экземплярах. Пожалуй, легче всего описать этот метод, как такой, у которого нет доступа к this и имеется ключевое слово static в его объявлении.

Однако тут возникает вопрос. Если у функции нет доступа к this, как же вызвать this.setState? Ответ заключается в том, что ничего такого вызывать и не нужно. Функция лишь должна вернуть обновлённые данные состояния, или, если обновление не требуется, null:


Использование getDerivedStateFromProps для обновления состояния

Возвращённое значение ведёт себя точно так же, как текущее значение setState — нужно лишь вернуть изменённую часть состояния, все остальные значения останутся такими же, как были.

Полезные советы по использованию getDerivedStateFromProps


?Не забудьте инициализировать состояние



Помните о необходимости инициализации состояния

Инициализацию исходного состояния компонента никто не отменял. Делать это надо либо в конструкторе, либо путём настройки поля класса.

?Особенности вызова getDerivedStateFromProps


Рассматриваемая функция вызывается и при первоначальном монтировании, и при перерисовке компонента, поэтому вы можете использовать её вместо создания в конструкторе состояния, основанного на свойствах.

?Ошибка при совместном использовании getDerivedStateFromProps и componentWillReceiveProps



Предупреждение, в котором рекомендуется использовать только getDerivedStateFromProps

Если вы объявите и getDerivedStateFromProps, и componentWillReceiveProps, будет вызвана лишь функция getDerivedStateFromProps, а в консоли появится предупреждение.

?О коллбэках и componentDidUpdate


Если обычно вы применяете коллбэки для того, чтобы обеспечить выполнение некоего кода после успешного обновления состояния, теперь, вместо коллбэков, используйте componentDidUpdate.

?О ключевом слове static


Если вы предпочитаете не использовать ключевое слово static, можете использовать альтернативную форму записи:


Объявление getDerivedStateFromProps без использования ключевого слова static

Новый компонент StrictMode


Строгий режим React представлен компонентом, который доступен по адресу React.StrictMode. Его можно добавить в дерево или поддерево приложения:


Использование use strict… Ох, не то. Мы, конечно, имеем в виду компонент StrictMode

Смысл использования StrictMode заключается в том, что благодаря наличию этого компонента система помогает обеспечивать соответствие кода рекомендованным подходам к разработке.

Если один из дочерних компонентов, выведенных в поддереве StrictMode, использует некоторые из методов, упомянутых выше (вроде componentWillMount), вы увидите сообщение об ошибке в консоли браузера при запуске проекта в режиме разработки:


Ошибка, сообщающая об использовании небезопасных методов жизненного цикла в поддереве StrictMode

Сейчас сообщение об ошибке указывает на RFC, где говорится об удалении методов жизненного цикла.

Новый компонент AsyncMode


Пока ещё неактивный механизм поддержки асинхронных компонентов был переименован так, чтобы его имя соответствовало имени компонента StrictMode. Теперь этот компонент можно найти по адресу React.unsafe_AsyncMode. Использование AsyncMode также активирует уведомления, характерные для StrictMode.

Если вы хотите узнать подробности об асинхронных компонентах React, загляните сюда и сюда.

Новая версия инструментов разработчика React


В дополнение к вышеописанным новшествам, в релизе React, о котором мы говорим, была представлена новая версия инструментов разработчика, которая поддерживает отладку новых компонентов.

Если вы используете Chrome, то вам понадобится немного подождать, так как соответствующее расширение в Интернет-магазине Chrome пока не обновлено, а попытки отлаживать старыми инструментами новый код приводят к… интересным результатам:


Рекорд React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED пока ещё не побит

А вот пользователи Firefox уже могут отлаживать новые конструкции:


Новый асинхронный компонент можно видеть при отладке в Firefox

Итоги: самое интересное впереди


Тут мы рассмотрели новшества альфа-релиза React 16.3, однако, стабильная версия этого релиза может содержать в себе другой набор изменений. Если ориентироваться на слова Дэна Абрамова по этому поводу, то React 16.3.0 должен выйти совсем скоро.


На прошлой неделе говорили о следующей неделе, которая уже наступила

Уважаемые читатели! Что вы думаете о новых возможностях React?

Комментарии (50)


  1. vitvad
    12.02.2018 19:14
    +2

    одному мне кажется очень странным удаление хуков componentWillMount & componentWillUpdate ?


    1. NicoBurno
      12.02.2018 20:16

      Если обновление state на основе props вынесено в getDerivedStateFromProps, то эти хуки теперь избыточны:

      • Вместо componentWillMount — для сайд-эффектов более оптимальным вариантом будет componentDidMount, а для работы со state и props можно использовать конструктор
      • Вместо componentWillUpdate — для сайд-эффектов предлагается componentDidUpdate, а для работы со state нам дают getDerivedStateFromProps


      1. vitvad
        12.02.2018 22:03

        просто реакт заявляет об обратной совместимости и «мы не такие как ангулар», но при этом переход с 15+ на 16 для основной массы готовых библиотек и компонентов занял пол года, это при том что там впринципе не было ничего сверхестественного только Props вынесли и еще по мелочи (я говорю с точки зрения совместимости старого кода, а не про новый Fiber), а тут изменение API которому 100 лет в обед и на которое, я уверен, завязано достаточно много логики в старом коде, и мне кажется, обновляться они будут еще дольше + не будет возможности обновить библиотеку компонентов на последнюю версию, сохранив старую версию реакта.

        Объединение componentWillMount и componentWillReceiveProps под одним getDerivedStateFromProps имеет смысл, что бы убрать дублирование кода.

        Если говорить без привязки к фреймворкам наличие у компонента

        хуков
        — init

        — willGetParams
        — shouldChange?
        — willMount/willUpdate
        ****** render
        — didUpdate

        — willUnmount


      1. Hazrat
        13.02.2018 10:44
        -1

        getDerivedStateFromProps не имеет доступа к this, соответственно не может обновлять state, он вроде для определения перерендерить компонент или отменить рендер


        1. mayorovp
          13.02.2018 10:59

          Вообще-то, обновлять state — его основная функция. Только делает это он возвращая значение, а не через setState.

          А определением перерендерить или отменить рендер занимается shouldComponentUpdate же…


    1. justboris
      13.02.2018 12:58

      А для чего вы ими пользовались?


      Для большинства операций больше пододят did-хуки, то есть componentDidMount и componentDidUpdate. Если нужно предотвратить обновление, то есть shouldComponentUpdate.
      will-версии и так не рекомендовались к использованию.


  1. alex6636
    12.02.2018 23:53

    Не кисло так за 4 года 16 версия, они хоть немного совместимы между собой?


    1. Valery4
      14.02.2018 19:00

      Вообще-то они после версии 0.14 выпустили версию 15 вследствие перехода на semver


    1. Valery4
      14.02.2018 19:02

      Вас не удивляет, что Хром за неполных десять лет добрался до версии 64?


  1. TheShock
    13.02.2018 00:54

    Новый контекст теперь работает только в рендере? Если да, то это редкостная кривизна. Тот же MobX'овый Inject как теперь должен работать?


    1. parmactep
      13.02.2018 01:16

      Суть в том что теперь есть обёртка в виде Consumer. А данные из контекста передаются дальше в виде props.


      1. TheShock
        13.02.2018 02:52

        В виде каких пропс? Вы видели пример? Они передаются в виде аргументов функции-как-потомка. В пропс ничего не передается и из других методов класса контекст не получить


        1. mayorovp
          13.02.2018 08:52

          Что-то мешает взять пропсы и передать дочернему компоненту?


          1. faiwer
            13.02.2018 09:11

            Изобретаем HOC? Мешает то, что получаем ещё 1 уровень вложенности, который усложняет и debugging, и несущественно, но всё же влияет на производительность. У нас и так в сложном приложении повсюду возможны матрёшки из HOC, а так матрёшками нужно покрывать компоненты, которые используют context. Добавить к этому какие-нибудь styled-components, где матрёшкой обёрнут каждый тег со стилями, и получается, что древо компонент почти целиком состоит из композитных матрёшек. С точки зрения приложения — ок, работает же. С остальных точек зрения — спорно, очень спорно.


            1. mayorovp
              13.02.2018 09:14

              Что значит «изобретаем»? Декоратор inject в mobx-react — это уже HOC, независимо от того нравится вам это или нет.


              1. faiwer
                13.02.2018 09:18

                А я про inject в mobx-react и connect в redux-react и не заикался. Это изначально сильно impure-вещи, которые идут как большой компромисс между идеализмом и практикой. Я скорее про тот context, что разработчики по своей воле сами используют в сложных проектах, устав от props-hell. Ниже отписал подробнее.


                1. mayorovp
                  13.02.2018 09:19

                  Зато TheShock именно про него и говорил, а я ему отвечал.


            1. bgnx
              13.02.2018 19:04

              Изобретаем HOC? Мешает то, что получаем ещё 1 уровень вложенности, который усложняет и debugging, и несущественно, но всё же влияет на производительность. У нас и так в сложном приложении повсюду возможны матрёшки из HOC, а так матрёшками нужно покрывать компоненты, которые используют context. Добавить к этому какие-нибудь styled-components, где матрёшкой обёрнут каждый тег со стилями, и получается, что древо компонент почти целиком состоит из композитных матрёшек. С точки зрения приложения — ок, работает же. С остальных точек зрения — спорно, очень спорно..

              Используйте для HOC наследование вместо композиции и никаких новых уровней вложенности добавляться не будет


              1. faiwer
                13.02.2018 19:07

                Вы знаете, я бы с радостью. Но вот не совсем понимаю, как его тут применить. Судя по тем обрывкам, что я увидел касательно нового API, он работает именно в рамках .render-метода. Ну и по сути асинхронно. Не подскажите рецепт? В голову приходят только разного рода извращения, работающие с задержкой.


                1. bgnx
                  13.02.2018 19:20
                  -1

                  Да просто использовать те же хокки но только не в виде


                  function hoc(TargetCmponent){
                    return class extends React.Component {
                     render(){
                       return <TargetCmponent {...this.props}/>
                     }
                   }
                  }

                  а в виде


                  function hoc(target){
                    const isTargetClass = target instanceof React.Component
                    return class extends (isTargetClass ? target : React.Component) {
                      render(){
                          return isTargetClass ? super.render() : target(this.props) 
                      }
                    }
                  }

                  и все, больше никаких вложенностей и глубоких матрешек из компонентов-оберток — сколько бы хокков мы бы не навесили на компонент — да хоть тысячу — в рантайме все равно будет создан только один компонент что будет быстрее, так как вызов рендера не создаст тысячу объектов-элементов виртуального дума для этих врапперов, и реакту нужно будет выполнить diff одного компонента а не тысячи


                  1. faiwer
                    13.02.2018 19:43

                    Вы, вероятно, имели ввиду примерно это:


                    function hoc(Target, key)
                    {
                        const isTargetClass = target instanceof React.Component;
                        const Parent = isTargetClass ? Target : React.Component;
                        return class extends Parent 
                        {
                            render()
                            {
                                return <Consumer>{val => 
                                {
                                    const props = {...this.props, [key]: val };
                                    return isTargetClass 
                                        ? super.render(props) 
                                        : target(props);
                                }}</Consumer>
                            }
                        };
                    }

                    Однако в данном случае мы располагаем нужным нам context-полем только внутри super.render. А сложные stateFull компоненты хотят его в других своих методах, включая, возможно, всякие хуки. В теории можно и это обойти, к примеру, задействовав setState. Но тогда мы должны писать компонент из рассчёта на двойной render при инициализации, и, возможно, при одновременной смене чего-нибудь в props и в context (но это уже редкий use case).


                    Идём дальше. Что если нам нужно несколько полей, а не одно? Нужно городить уже достаточно сложную обвязку. Всё выполнимо, но от этого кода сильно несёт… каким-то альтернативным react-ом.


                    1. bgnx
                      13.02.2018 19:57

                      Ну да, с новым контекстом реакта доступ возможен только внутри render-метода. То что этот контекст не будет доступен в разных хуках это проблемы нового контекста. А вообще, если нужно связывать несколько полей, то зачем нужен контекст — не лучше ли использовать внешнее состояние? Я выше привел вариант hoc-ов когда вместо композиции мы используем наследование и избегаем компонентов-оберток в рантайме что позволит улучшить производительность и каких-то минусов по сравнению композиционными hoc-ами я не вижу


                      1. faiwer
                        13.02.2018 20:07

                        А вообще, если нужно связывать несколько полей, то зачем нужен контекст — не лучше ли использовать внешнее состояние?

                        Вы имеете внешнее хранилище? Ну вот я использую redux. Но там данные приложения. А вещи которые я пробрасываю через context "руками" другого характера. Сложно объяснить. Ну кроме одной — i18n, по сути переводы и инструментарий к ним.


                        Да можно написать свой внешний context, по принципу того же redux-react-а. Но это что-то вроде из стрельбы пушки по воробьям. Серьёзное архитектурное решение.


                        Старый context при всей своей уродливости был в чём-то изящен. Идеологически. Предполагалось, что туда помещают либо совсем что-то impure, и тогда сами ковыряйтесь в этом как хотите (react-redux скажем), либо что-то статическое, и тогда вся машинерия по re-render-у уже не нужна, а вот от килотонн бойлерплейта избавляет.


                        А новый контекст гораздо больше напоминает прицельные порталы для изменяемых значений. Теперь наследник окольными путями (скажем через import-export) узнаёт что-то от родителя (react в этом совсем не помогает), и позволяет от этого асинхронным образом в render методе зависеть. Вероятно, это выглядит достаточно изящно, при решении каких-то задач. Не знаю.


                        По поводу внешнего хранилища. Тут ещё встаёт вопрос реализации. Скажем redux-react очень уязвим в вопросе количества подписчиков. Скажем если у вас приложение рендерит сотни или тысячи мелких компонент, то подписываясь в каждом из них на store, вы заставляете connect вычислять mapStateToProps на любой чих (коих может быть очень много). В лучшем случае это просто будет жрать батарейку, в худшем ещё и тормозить.


                        1. bgnx
                          13.02.2018 20:26

                          Ну контекст это по сути просто объект в который помечаются общие для какого-то поддерева компонентов данные. Если данные контекста не зависят от того в каком поддереве находится компонент например i18n и другие данные то мы можем просто вынести этот контекст во внешний объект в отдельном файле выполнить заимпортить этот объект и обращаться к нему напрямую. Если контекст у разных поддеревьях компонентов разный то всегда можно выразить эту сутуацию не в через компоненты а через данные.


                          По поводу внешнего хранилища. Тут ещё встаёт вопрос реализации. Скажем redux-react очень уязвим в вопросе количества подписчиков. Скажем если у вас приложение рендерит сотни или тысячи мелких компонент, то подписываясь в каждом из них на store, вы заставляете connect вычислять mapStateToProps на любой чих (коих может быть очень много). В лучшем случае это просто будет жрать батарейку, в худшем ещё и тормозить.

                          O, вы обратились по адресу — в статьях тут, тут и тут я разбирал эту проблему и ее решение в виде mobx


                          1. faiwer
                            13.02.2018 20:34

                            Если контекст у разных поддеревьях компонентов разный то всегда можно выразить эту сутуацию не в через компоненты а через данные.

                            В случае MobX ? может быть. В случае redux это как-то дорого обходится.


          1. faiwer
            13.02.2018 09:16

            А старая концепция context-а, предполагала, что в нём лежат статичные данные, нечто вроде констант, которые не требуется передавать явным образом. Естественно я не имею ввиду redux и прочие динамичные штуки, а скорее какой-нибудь i18n и ему подобные вещи. Это касается в первую очередь сложных приложений, где такой подход работы с контекстном позволяет избежать много-много-многочисленных пробрасываний каких-то обобщённых props по всему большой вложенности древу вниз.


            Новый же context, если я правильно понял, вполне себе динамический. Что-то вроде портала для отдельно взятых значений. Которые при этом не зазорно и менять в родительских render-ах. Получившаяся штука выходит довольно могучей, но решает она, явно какие-то другие задачи.


            1. mayorovp
              13.02.2018 09:21

              А i18n и подобные вещи — это тоже вполне себе изменяемые данные, пусть и нечасто изменяемые.


              1. faiwer
                13.02.2018 09:26

                Ок, допустим. На самом верхнем уровне, где лежит какой-нибудь <i18n-provider/> можно просто в случае "динамики" (например смены языка) проставить новый key и, хотя мы разово перетрясём всё vdom-древо, мы добьёмся нужных результатов. При том, что на кнопку хорошо если хоть кто-нибудь нажмёт. Мне такой подход показался разумным компромиссом. А теперь же для обеспечения того же функционала, нужно (в моих проектах) покрывать HOC каждый 3-й компонент. Contex позволял обойтись малыми жертвами. А случае использования наследования давался практически бесплатно.


            1. faiwer
              13.02.2018 09:33

              Хм. Отдельный момент эти сами .consumer-ы. Если я ничего не напутал, то их нужно передавать по ссылке. Т.е. самым явным образом. Скажем при помощи import-export-а, а это, в свою очередь, усложняет жизнь, не просто лишним бойлерплейтом, а ещё и тем, что если у нас один parent-component, мог порождать несколько вариаций context-значений, исходя из условий, то теперь надо будет как-то самостоятельно разграничивать их своими силами, и react нам в этом больше не помощник.


              Нет, я конечно ожидал, что context пересмотрят, но чтобы настолько… необычно. Этого вот я не ждал. Мне даже сложно назвать это контекстом. Скорее порталом :)


          1. TheShock
            13.02.2018 20:42
            +1

            Что-то мешает взять пропсы и передать дочернему компоненту?

            Вы понимаете зачем нужен контекст? Чтобы не пользоваться пропсами, для того, чтобы гонять по дочерним компонентам всякие вещи типа сторов. По сути, это единственный способ нормального DI в Реакте.

            А вот вы мне предлагаете, если я хочу воспользоваться контекстом, чтобы не использовать пропсы — использовать пропсы? Тогда зачем вообще нужен такой костыльный контекст, если нужно просто передавать во все компоненты в пропсы еще и контекст вручную?


    1. mayorovp
      13.02.2018 08:51

      А что случилось-то? Ну делал он раньше var additionalProps = grabStoresFn(this.context.mobxStores || {}, newProps, this.context) || {}, а теперь будет делать


      <MobxStores.Consumer>
          {(mobxStores) => {
              var additionalProps = grabStoresFn(mobxStores, newProps)
              // ...
              return createElement(component, newProps)
          }}
      </MobxStores.Consumer>

      Или вы про то, что третий аргумент неоткуда взять теперь? А его вообще кто-то использует? Всегда же он был в состоянии "недодокументирован"...


      1. TheShock
        13.02.2018 20:43
        +1

        А случилось то, что раньше я мог в любом методе класса обратиться к контексту, а сейчас могу обратиться только в части метода рендер. При чем тут третий аргумент вообще?


        1. mayorovp
          13.02.2018 20:54

          Вы спрашивали как должен работать inject из mobx-react. Я ответил как.


          1. TheShock
            13.02.2018 20:59

            Всмысле «ответили»? Вы совершенно не ответили, как inject будет работать так, чтобы я мог из всех методов пользоваться зависимостями.


            1. mayorovp
              13.02.2018 21:25

              В таком случае я не понимаю как вы использовали inject до этого… Документация говорит что его надо использовать вот так:


              @inject("color") @observer
              class Button extends React.Component {
                render() {
                  return (
                    <button style={{background: this.props.color}}>
                      {this.props.children}
                    </button>
                  );
                }
              }

              То есть зависимости попадают в props, откуда их можно достать в любом методе.


              1. TheShock
                13.02.2018 21:46

                Вот именно что «в любом методе».

                То есть я могу использовать его так:

                @inject("counter") @observer
                class MyComponent extends Component<{ id: number, counter: CounterStore }> {
                  onButtonClick() {
                    const { id, counter } = this.props;
                	
                    counter.get(id).increaseAction();
                  }
                
                  render() {
                    const { id, counter } = this.props;
                  
                    return (
                      <CoolButton onClick={this.onButtonClick}>
                        {counter.get(id).value}
                      </CoolButton>
                    );
                  }
                }


                Видите? В любом методе класса, а не только в части метода рендер.


                1. mayorovp
                  13.02.2018 22:01

                  Раньше inject передавал вам в props тот counter который он достал из контекста в рендере. Теперь inject будет передавать вам в props тот counter который достал Consumer в рендере.

                  Что поменялось-то?


                  1. TheShock
                    13.02.2018 22:13

                    Посмотрите как внутри inject устроен

                    Хм, вероятно вы правы, а я — нет. Я заглядывал, но давно. Странно, мне казалось, что он иначе устроен. Последнее время менялся подход?


  1. n0ne
    14.02.2018 12:34

    Извините, конечно, может что-то не так понял, но как достучаться до this.state в getDerivedStateFromProps?! this же недоступен…


    1. mayorovp
      14.02.2018 12:37

      Он же передается вторым параметром…


      1. n0ne
        14.02.2018 13:29

        Тупикнул, спасибо (-:


    1. faiwer
      14.02.2018 12:41

      До this.state там дотягиваться и не нужно, т.к. он передаётся как параметр метода. А вот до всех остальных полей компонента уже по-нормальному никак. Довольно странное решение, если учесть, в документации открытым текстом сказано, что не стоит помещать в .state поле ничего, что не участвует в .render, мол помещайте "как есть" в this.%property%. Поместили. А теперь туда доступа нет :)


      1. n0ne
        14.02.2018 13:37

        Да, уже понял про второй параметр, спасибо… хотя…
        Как сравнить this.props и nextProps? Вернее, где теперь сравнить props, пришедшие, например, из redux??!


      1. unel
        14.02.2018 13:52
        +1

        ну, для помещения чего-то в this.%property%, я так понимаю, оставили componentDidUpdate… (если это действительно так необходимо)


        1. faiwer
          14.02.2018 16:57
          +1

          Кстати да. В большинстве не сильно хитрых случаев componentDidUpdate вполне сгодится. Правда вот порылся в своём коде и увидел что у меня есть методы componentWillReceiveProps, которые оперируют offsetWidth и offsetHeight от <SVG/> для формирования нового state. Теперь чтобы иметь к ним доступ нужно этот DOMElement толкать в state. В раздумьях. Насколько здравая идея толкать в .state DOM-элементы, а не данные. Логика подсказывает, что ничего криминального.


          1. mayorovp
            14.02.2018 17:28

            Как минимум — лишний рендер (либо WTF-логика в shouldComponentUpdate), ведь DOM-элемент можно получить только после рендера, а изменение state вызывает рендер.


            1. faiwer
              14.02.2018 17:49

              ведь DOM-элемент можно получить только после рендера

              Дак и componentWillReceiveProps тоже вызывается уже после первого render-а. А касательно того, что DOMElement раньше не получить ? что есть, то есть :) Так уж React устроен.


  1. faiwer
    15.02.2018 14:25

    Интересный момент есть, связанный с <Provider/>-ми. Представьте, что вы maintainer redux-а. И вам нужно переехать на новый API. Вы, естественно не хотите, чтобы пользователи из-за обновления стали переписывать все свои container-а. Но что делать? Ведь если мы будем порождать новый Context.{Provider|Consumer} для каждого store-а, а внутри реализации connect-обёртки мы не будем иметь доступ к соответствующему <Consumer/>, и всё накроется медным тазом. Нужно будет пробрасывать store повсеместно. Т.е. переписывать все контейнера. Да и переиспользуемость компонент накроется медным тазом тоже. Они будут явным образом зашиты на один конкретный store.


    Оказалось, что всё намного проще. redux-react может позволить себе один единственный <Provider/> на всю свою библиотеку, сколько бы store-ов у вас не было. И createContext будет вызван именно внутри библиотеки. Всё дело в том, что один и тот же <Provider/> можно вкладывать в себя же самого сколько душе угодно, и всякий раз при этом менять значение. А каждый <Consumer/> будет иметь доступ только к тому значению, которое ближайшее к нему по древу.


    Т.е. при первоначальном взгляде на <Provider/> он напоминает телепорт значения сверху внизу, минуя props. На деле же каждый ReactElement порождённый <Provider/>-классом является самостоятельной сущностью со своим собственным значением. Указав во внутреннем <Provider/>-е другое значение, мы значение верхнего не изменим, и вообще ничего не сломается. Они не будут иметь с друг другом ничего общего. Хотя ссылки на <Provider/>-ы и <Cosumer/>-ы будут ===.


    1. mayorovp
      15.02.2018 14:43

      Ну это как бы было очевидно…


      1. faiwer
        15.02.2018 14:47

        Мне нет. Даже в голову не пришло. Более того я уже столько материалов на эту тему нарыл, столько перечитал. Перерыл кучу обсуждений на github-е, RFC посмотрел и все доводы за и против, мотивы решения. Даже стал песочницу собирать. А потом случайно в комментариях на Medium наткнулся на вопрос про вложенность и даже ссылку на рабочий sandbox. И только поковыряв его до меня дошло, как на деле я буду его использовать (несколько вложенных instance от одного и того же Provider-а в одном древе).