СоХабр закрыт.

С 13.05.2019 изменения постов больше не отслеживаются, и новые посты не сохраняются.

H Введение в React Hooks в черновиках Перевод



Если вы читаете Twitter, вы, вероятнее всего, знаете что Hooks  -  это новая фича React, но вы можете спросить, как мы на практике можем их использовать? В этой статье мы покажем вам несколько примеров использования Hooks.
Одна из ключевых идей для понимания заключается в том, что Hooks позволяют использовать state и другие возможности React без написания классов.

Мотивация стоящая за Hooks


Хотя компоненто-ориентированная архитектура позволяет нам повторно использовать view в нашем приложении, одна из самых больших проблем, с которыми сталкивается разработчик, заключается в том, как повторно использовать логику, находящуюся в state, между компонентами. Когда у нас есть компоненты, которые имеют сходную логику состояния, нет хороших решений для переиспользования компонентов, и это иногда может привести к дублированию логики в конструкторе и методах жизненного цикла.
Чтобы решить эту проблему, обычно используют:

  • компоненты высшего порядка (High Order Components)
  • render props

Но оба эти паттерна имеют недостатки, которые могут способствовать усложнению кодовой базы.

Hooks нацелены на решение всех этих проблем, позволяя вам писать функциональные компоненты, которые имеют доступ к state, context, методам жизненного цикла, ref и т. д., без написания классов.

Hooks в Alpha


Прежде чем мы погрузимся, важно упомянуть, что разработка Hooks API еще не завершена.

Кроме того, официальная документация очень хороша, и мы рекомендуем ее прочитать еще и потому, что в ней расширенно описана мотивация, стоящая за введением Hooks.

Как Hooks соотносяться с классами


Если вы знакомы с React, один из лучших способов понять Hooks - это посмотреть, каким образом мы можем воспроизвести поведение, с которым мы привыкли работать при использовании классов, используя Hooks.

Напомним, что при написании классов компонентов нам часто необходимо:

  • Управлять state
  • Использовать методы жизненного цикла, такие как componentDidMount() и componentDidUpdate()
  • Доступ к context (static contextType)


С помощью React Hooks мы можем воспроизвести аналогичное поведение в функциональных компонентах:

  • Для доступа к состоянию компонента использовать useState() hook
  • Вместо использования методов жизненного цикла таких как componentDidMount() и componentDidUpdate(), использовать useEffect() hook.
  • Вместо static свойства contextType использовать useContext() hook.

Для использования Hooks необходима последняя версия React


Вы можете начать работу с Hooks прямо сейчас, сменив значение react и react-dom в вашем package.json на «next».



Пример useState() Hook


State является неотъемлемой частью React. Он позволяет нам объявлять переменные, которые содержат данные, которые, в свою очередь, будут использоваться в нашем приложении. С помощью классов state обычно определяется следующим образом:



До Hooks, state обычно использовался только в компоненте - классе, но, как упоминалось выше, Hooks позволяет нам добавлять состояние и к функциональному компоненту.
Давайте посмотрим пример ниже. Здесь мы построим переключатель с подсветкой, который изменит цвет в зависимости от значения state. Для этого мы будем использовать hook useState().
Вот полный код (и исполняемый пример) - мы рассмотрим, что происходит ниже. По клику на изображение можно посмотреть на этот пример на CodeSandBox.



Наш компонент - это функция


В вышеприведенном блоке кода, мы начинаем с импорта useState из React. UseState  —  это новый способ использования возможностей, которые раньше могло предложить this.state.
Затем обратите внимание, что этот компонент является функцией, а не классом. Интересно!

Чтение и запись state


Внутри этой функции мы вызываем useState для создания переменной в state:



useState используется для объявления переменной state и может быть инициализирован любым типом значения (в отличие от state в классах, который должен быть объектом).

Как видно выше, мы используем деструктуризацию по возвращаемому значению useState.
  • Первое значение, light в этом случае, является текущим state (как this.state)
  • Второе значение - это функция, используемая для обновления значения state(первого значения) (как this.setState).

Затем мы создаем две функции, каждая из которых устанавливает state в разные значения, 0 или 1.



Затем мы применяем их как обработчики событий на кнопках во view:



React отслеживает state


Когда нажимается кнопка «On», Вызывается функция setOn, вызывающая setLight(1). Вызов setLight(1) обновляет значение light для следующего рендера. Это может показаться немного волшебным, но React отслеживает значение этой переменной и будет передавать новое значение, когда происходит ре-рендер этого компонента.
Затем мы используем текущее состояние (light), чтобы определить, должна ли лампа быть включена или нет. То есть, мы устанавливаем цвет заливки SVG в зависимости от значения light. Если light равен 0 (выкл.), То для fillColor установлено значение # 000000 (а если равен 1 (включено), fillColor устанавливается на # ffbb73).

Multiple States


Хотя мы не делаем этого в приведенном выше примере, вы можете создать несколько state, вызвав useState более одного раза. Например:



ПРИМЕЧАНИЕ. 
Существуют некоторые ограничения при использовании hooks, о которых вы должны знать. Самое главное, вы должны вызывать hooks только на верхнем уровне вашей функции. См. «Правила hooks» для получения дополнительной информации.


Пример useEffect() Hook


UseEffect Hook позволяет выполнять side эффекты в функциональных компонентах. Побочными эффектами могут быть обращения к API, обновление DOM, подписка на обработчики событий - все, что хотите, лишь бы произошло «императивное» действие.

Используя useEffect() Hook, React знает, что вы хотите выполнить определенное действие после рендеринга.

Давайте посмотрим на пример ниже. Мы будем использовать useEffect() для вызова API и получения ответа.



В этом примере кода используются как useState, так и useEffect, и это потому, что мы хотим записать результат вызова API в state.



Получение данных и обновления state


Чтобы «использовать эффект», нам нужно поместить наш action в функцию useEffect, то есть мы передаем «action» эффект как анонимную функцию, как первый аргумент useEffect.
В примере выше мы обращаемся к API, которое возвращает список имен. Когда возвращается response, мы конвертируем его в JSON, а затем используем setNames(data) для установки state.



Проблемы с производительностью при использовании Effects



Однако стоит сказать еще кое-что об использовании useEffect.

Первое, о чем нужно подумать, это то, что по умолчанию наш useEffect будет вызываться на каждом рендере! Хорошей новостью является то, что нам не нужно беспокоиться об устаревших данных, но плохая новость заключается в том, что мы, вероятно, не хотим делать HTTP-запрос для каждого рендеринга (как в этом случае).

Вы можете пропустить effects, используя второй аргумент useEffect, как и в этом случае. Второй аргумент useEffect - это список переменных, которые мы хотим «наблюдать», а затем мы будем повторно запускать эффект только при изменении одного из этих значений.

В приведенном выше примере кода обратите внимание, что мы передаем пустой массив в качестве второго аргумента. Это мы говорим React, что мы хотим только назвать этот effect при монтировании компонента.

Чтобы узнать больше о производительности effect, ознакомьтесь с этим разделом в официальных документах.

Кроме того, как и функция useState, useEffect позволяет использовать несколько экземпляров, что означает, что вы можете иметь несколько функций useEffect.

Пример useContext() Hook


Точка контекста

Контекст в React- это способ для дочернего компонента получить доступ к значению в родительском компоненте.

Чтобы понять необходимость context: при создании React приложения вам часто нужно передавать значения с верха вашего дерева React вниз. Не используя context, вы передаете props через компоненты, которым не обязательно о них знать.

Передача props вниз по дереву «несвязанных» компонентов ласково называется props drilling.
React Context решает проблему props drilling, позволяя вам делиться значениями через дерево компонентов, с любым компонентом, который запрашивает эти значения.

useContext() упрощает использование context

С useContext Hook использование context стает проще, чем когда-либо.

Функция useContext() принимает объект сontext, который изначально возвращается из React.createContext(), а затем возвращает текущее значение контекста. Давайте посмотрим на пример ниже.



В приведенном выше коде context JediContext создается с использованием React.createContext().

Мы используем JediContext.Provider в нашем App компоненте и устанавливаем там значение «Luke». Это означает, что любой компонент, которому нужно получить доступ к context теперь сможет считать это значение.

Чтобы прочитать это значение в функции Display(), мы вызываем useContext, передавая аргумент JediContext.

Затем мы передаем объект context, который мы получили из React.createContext, и он автоматически выводит значение. Когда значение провайдера будет обновляться, этот Hook автоматически сработает с последним значением context.

Получение ссылки на context в более крупном приложении


Выше мы создали JediContext в рамках обоих компонентов, но в более крупном приложении Display и App будут находиться в разных файлах. Поэтому, если у вас похожая ситуация, вам может быть интересно: «Как мы получаем ссылку на JediContext между файлами?»

Ответ заключается в том, что вам нужно создать новый файл, который экспортирует JediContext.
Например, у вас может быть файл context.js, который содержит что-то вроде этого:



и потом в App.js (и Display.js) вы должны написать:



Спасибо, Дейв)

Пример useRef() Hook


Refs предоставляет способ доступа к React элементам , созданным в методе render().
Если вы новичок в React refs, вы можете прочитать это введение в React refs.
Функция useRef() возвращает объект ref.



useRef() и формы с input


Давайте посмотрим пример использования useRef() hook.



В приведенном выше примере мы используем useRef() в сочетании с useState(), чтобы отрендерить значение input в тег p.

Ref создается в переменной nameRef. Затем переменную nameRef можно использовать в input, задав как ref. По существу, это означает, что теперь содержимое поля ввода будет доступно через ref.

Кнопка отправки в коде имеет обработчик события onClick, называемый submitButton. Функция submitButton вызывает setName (созданный через useState).

Как мы уже делали с использованием hookState, setName будет использоваться для установки state name. Чтобы извлечь имя из тега input, мы читаем значение nameRef.current.value.

Еще одно замечание относительно useRef заключается в том, что его можно использовать больше, чем атрибут ref.

Использование пользовательских Hooks


Одной из самых крутых особенностей Hooks является то, что вы можете легко делиться логикой между несколькими компонентами, создавая собственный Hook.

В приведенном ниже примере мы создадим пользовательский setCounter() Hook, который позволяет нам отслеживать состояние и предоставлять настраиваемые функции обновления state!

См. Также, этот useCounter Hook от react-use и useCounter от Кента




В приведенном выше блоке кода мы создаем функцию useCounter, которая хранит логику нашего hook.

Обратите внимание, что useCounter может использовать другие Hooks! Начнем с создания нового состояния Hook через useState.

Затем мы определяем две вспомогательные функции: increment и decrement, которые вызывают setCount и соответственно корректируют текущий count.

Наконец, мы возвращаем ссылки, необходимые для взаимодействия с нашим Hook.

В: Что происходит, возврат массива с объектом?
О: Ну, как и большинство вещей в Hooks, соглашения API еще не завершены. Но то, что мы делаем здесь, возвращает массив, где:

  • Первый элемент - текущее значение Hook
  • Второй элемент - это объект, содержащий функции, используемые для взаимодействия с Hook.

Это соглашение позволяет вам легко «переименовать» текущее значение Hook - как мы делаем выше с помощью myCount.

Тем не менее, вы можете вернуть все, что захотите, из своего кастомного Hook.

В приведенном выше примере мы используем increment и decrement как обработчики onClick, в нашем view. Когда пользователь нажимает кнопки, счетчик обновляется и повторно отображается (как myCount) во view.

Написание тестов для React Hooks


Чтобы написать тесты для hooks, мы будем использовать библиотеку для тестирования react-testing-library.

react-testing-library - очень легковесное решение для тестирования компонентов React. Она является раширением react-dom and react-dom/test-utils. Использование библиотеки react-testing-library гарантирует, что ваши тесты будут работать непосредственно с DOM узлами.

С тестированием hooks еще не все понятно. В настоящее время вы не можете протестировать hook изолированно. Вместо этого вам нужно прикрепить свой hook к компоненту и протестировать этот компонент.

Итак, ниже мы будем писать тесты для наших Hooks, взаимодействуя с нашими компонентами, а не с Hooks напрямую. Хорошая новость заключается в том, что наши тесты будут выглядеть как обычные тесты React.

Тестирование useState() Hook


Давайте посмотрим пример написания тестов для useState Hook. В приведенном выше уроке мы тестируем больше вариаций используемого выше примера useState. Мы будем писать тесты, чтобы убедиться, что нажатие кнопки «Off» Устанавливает состояние в 0 и нажатие кнопки «On» Устанавливает состояние в 1.



В вышеприведенном блоке кода мы начинаем с импорта некоторых хелперов из react-testing-library и тестируемого компонента.

  • render, это поможет отобразить наш компонент. Он рендерится в контейнер, который добавляется к document.body
  • getByTestId получает DOM элемент по data-testid
  • fireEvent, это используется для «запуска» событий DOM. Она прикрепляет обработчик события к document и обрабатывает некоторые события DOM через делегирование событий, например. нажав кнопку.

Далее, в функции утверждения теста, мы задаем значения переменных для элементов с data-testid и их значения, которые мы хотели бы использовать в тесте. Со ссылками на элементы на DOM мы можем затем использовать метод fireEvent для имитации щелчка по кнопке.

Тест проверяет, что, если нажимается onButton, значение state устанавливается в 1, а при нажатии на offButton state равен 1.

Тестирование useEffect() Hook 


В этом примере мы будем писать тесты, чтобы добавить товар в корзину, используя useEffect Hook. Количество элементов также сохраняется в localStorage. Файл index.js в CodeSandbox ниже содержит фактическую логику, используемую для добавления элементов в корзину.

Мы будем писать тесты, чтобы убедиться, что обновление количества элементов корзины также отражено в localStorage, и даже если страница перезагрузилась, количество элементов корзины все еще остается прежним.



В функции, подтверждающей прохождение теста мы сначала устанавливаем cartItem в localStorage равным 0, что означает, что количество элементов корзины равно 0. Затем мы получаем как container так и rerender из компонента App через деструктурирование. Rerender позволяет нам имитировать перезагрузку страницы.

Затем мы получаем ссылки на кнопки и тег p, который отображает текущее значение корзины и устанавливает их в переменные.

Как только это будет сделано, тест затем имитирует щелчок на addButton и проверяет, является ли текущий счетчик корзины равным 1 и перезагружает страницу, после чего, если он проверяет, установлено ли значение localStorage, cartItem, равным 1. Затем он моделирует нажатие на resetButton и проверяет, установлено ли текущее количество элементов корзины равным 0.

Тестирование useRef () Hook


В этом примере мы будем тестировать useRef Hook, и мы будем использовать исходный пример useRef, приведенный выше в качестве основы для теста. UseRef используется для получения значения из поля ввода, а затем устанавливает значение state. Файл index.js в CodeSandbox ниже содержит логику ввода значения и его отправки.



В функции, утверждающей прохождение теста мы устанавливаем переменные в поле input, тег p, который отображает текущее значение ref, и кнопку отправки. Мы также устанавливаем значение, которое мы хотели бы ввести в поле ввода, для переменной newName. Это будет использоваться для проверки в тесте.



Метод fireEvent.change используется для ввода значения в поле input, и в этом случае используется name, сохраненное в константе newName, после чего нажимается кнопка отправки.

Затем тест проверяет, соответствует ли значение ref после нажатия кнопки значение newName.

Наконец, вы должны увидеть «Нет падений тестов, поздравляем!» сообщение в консоли.

Реакция сообщества на Hooks


С того момента как представили React Hooks, сообщество было в восторге от этой фичи, и мы видели множество примеров и примеров использования React Hooks. Вот некоторые из основных:

  • На этом сайте собрана коллекция React Hooks.
  • react-use, библиотека, которая поставляется с кучей React Hooks.
  • В этом примере на CodeSandbox показано, как использовать useEffect Hook для создания анимаций с помощью react-spring
  • Пример использования useMutableReducer, который позволяет вам просто мутировать состояние, чтобы обновить его в редьюсере.
  • Этот пример на CodeSandbox, который показывает сложное комплексное использования связи родитель-ребенок и использование редьсеров.
  • Компонент переключения, построенный с помощью React Hooks
  • Другая коллекция React Hooks, в которой есть hooks для входных значений, ориентации устройств и видимости документа.


Различные типы hooks


Существуют различные типы hooks, которые вы можете начать использовать в своем React приложении. Они перечислены ниже:

  • useState  —  позволяет нам писать чистые функции с доступом к state в них.
  • useEffect  —  позволяет нам выполнять side эффекты. Side эффектами могут быть вызовы API, обновление DOM, подписка на обработчики событий.
  • useContext  -  позволяет писать в них чистые функции с контекстом.
  • useReducer  —  дает нам ссылку на Redux-подобный редьюсер
  • useRef  —  позволяет писать чистые функции, которые возвращают изменяемый объект ref.
  • useMemo  -  используется для возврата сохраненного значения.
  • useCallback —  Hook используется для возврата мемоизованного каллбека.
  • useImperativeMethods  -  кастомизирует значение экземпляра, которое передается родительским компонентам при использовании ref.
  • useMutationEffects  —  аналогичен useEffect Hook в том смысле, что он позволяет выполнять DOM-мутации.
  • useLayoutEffect  -  используется для чтения макета из DOM и синхронного ре-рендеринга.
  • Пользовательские hooks  -  позволяют писать компонентную логику в функции многократного использования.


Будущее hooks


Самое замечательное в Hooks заключается в том, что они работают бок о бок с существующим кодом, поэтому вы можете медленно вносить изменения, внедряющие Hooks. Все, что вам нужно сделать, - это обновить React до версии поддерживающей hooks.

Тем не менее, Hooks все еще являются экспериментальной функцией, и команда React неоднократно предупреждала, что API может быть изменен. Считайте что вы предупреждены.
Что означает для классов появление Hooks? Как сообщает команда React, классы все еще остаются, они являются огромной частью кодовой базы React и, скорее всего, будут еще какое-то время.

У нас нет планов осуждать классы. В Facebook у нас есть десятки тысяч компонентов, написанных классами, и, как и вы понимаете, мы не собираемся переписывать их. Но если сообщество React одобрит Hooks, нет смысла иметь два разных рекомендуемых способа записи компонентов —  Дэн Абрамов

Хотя конкретный API-интерфейс Hooks является экспериментальным сегодня, сообщество одобряет идею Hooks, поэтому я думаю, что они останутся с нами надолго.

Дополнительные ресурсы


  • Команда React сделала замечательную работу по документированию React Hooks, подробнее можете ознакомиться здесь
  • API из официальной документации здесь.
  • Есть также постоянный RFC, поэтому вы можете отправиться туда, чтобы задавать вопросы или оставлять комментарии
+12
~7400

комментарии (36)

+2
alexesDev ,  
Это огромный шаг в функциональную сторону, только смущает, что в React объекте теперь хранится текущий обрабатываемый узел дерева (в общем просто глобальная переменная). На порядок круче было бы принимать какой-то io объект как props:
const MyComponent = ({ props }, io) => {
  const [count, setCount] = io.useState('count');
};
0
vvadzim ,  
Тогда уже
const MyComponent = ({ props }, io) => {
  const [count, setCount] = useState(io, 'count');
};

чтоб свои хуки можно было делать единообразно.
Ну вот они подумали-подумали, и решили io убрать :)
0
alexesDev ,  
Так содержимое компонента лезет во внешний scope и нельзя мокать useState без костылей.
+2
staticlab ,  

Какую же функциональную, если эти хуки состоят из сайд-эффектов чуть менее, чем полностью?

–2
+1 –3
vvadzim ,  
Вы сравниваете функциональщину и сайд-эффекты, это как тёплое с мягким.
В том же чуть менее чем полностью функциональном хаскеле сайд-эффекты в монадах есть.
Можно сказать что react hooks — одна большая монада.
+3
mayorovp ,  

Эдак и про императивный код можно сказать что это одна большая монада. Отличие того что тут сделано от того что сделано в Хаскеле — в том, что там чистые функции не перестают быть чистыми от использования монад, а тут — перестают.


К примеру, функция put 5 в Хаскеле возвращает значение типа State Int (), и сколько бы раз ее ни вызывали — это будут совершенно эквивалентные State Int (). Если написать вот так — let a = put 5; b = put 5 in ..., то никаким тестовым сценарием вы не обнаружите разницы в поведении a и b.


А вот в Реакте все не так. Для начала, useState просто не получится вызвать за пределами функционального компонента. А после вызова он вернет уникальную пару значений, которая легко отличима от любой другой такой же пары...

–2
vvadzim ,   * (был изменён)
Не согласен. В хаскельской монаде два одинаковых последовательных вызова могут вернуть и возвращают два разных значения. Для того монады и придумали. Более того, за пределами монады такие вызовы невозможны, их компилятор не пропустит. В этом смысле useState ведёт себя очень по-монадовски.

ПС А имперавтивный код — ну да, одна большая монада.
+1
mayorovp ,   * (был изменён)

Вы путаете результат вызова функции и значение которое будет передано второму операнду операции >>=.


Так, get в монаде State всегда возвращает функцию \s -> (s,s), независимо от того сколько раз и когда вызывается.


А вот в выражении get >>= (\x -> ...) параметр x может оказаться связанным с любым значением. Но это никак не нарушает чистоту функции.

–2
vvadzim ,   * (был изменён)
Мы говорим о разных вещах. "=" в javascript не то же что "=" в haskell. Иногда это "<-". И тогда результат вызова функции зависит от состояния в монаде. Фактически, монада — один из аргументов функции в такой записи и этот аргумент добавляется неявно, и поэтому некорректно говорить о том, что два последовательных вызова функции с одними и теми же аргументами вернут одно и то же значение. Иначе например чтение чисел с клавиатуры было бы невозможно.
+2
TheShock ,  
если эти хуки состоят из сайд-эффектов чуть менее, чем полностью

Это уже просто смешно. Ладно раньше недалекие верующие не видели процедурности dispatch'a — там необходимо немного подумать, так что это для верующих простительно. Но ведь тут написан очевидный процедурный код, да даже с процедурными названиями!

const setOn = () => setLight(0)


Но все-равно божья роса, кричат, что это ФП.

Кстати, а как же оптимизация? Мы ведь каждый раз создаем функции setOn и setOff каждый раз и передает в компоненты ниже как props. В итоге, компонент тоже обязан перерисоваться, хотя, де факто, пропсы не менялись.
+2
faiwer ,   * (был изменён)

А там для этого есть дичьuseCallback и React.memo. После просмотра примеров придётся промыть глаза с мылом :)

0
TheShock ,  
Что самое интересное в ужасном MobX это решается совершенно без костылей. Привязка функции осуществляется только тогда, когда это необходимо и по вьюшкам передается один и тот же инстанс, сколько бы раз мы его не перерендерили или не пересоздавали.

class MyItem {
    @observable counter = 1;

    @action increase = () => this.counter++;
}

@observer
class MyItemRenderer extends React.Component {
  render () {
    const { item } = this.props;

    return <div onClick={item.increase}> {item.counter} </div>;
  }
}
+1
staticlab ,  

Так и код выглядит чище, и логика более очевидна.

0
mayorovp ,   * (был изменён)

В обсуждаемом примере count/counter — это состояние, а не свойство, так что лучше сравнивать с вот этим кодом:


@observer
class MyItemRenderer extends React.Component {
  @observable counter = 1;
  @action.bound increase() { this.counter++; }

  render () {
    return <div onClick={this.increase}> {this.counter} </div>;
  }
}
0
+1 –1
TheShock ,  
Почему лучше? useState — это квинтэссенция отвратительного дизайна. Какой смысл стараться повторить то, что они написали, если нету никакого смысла писать так, кроме желания повыё, как круто и функционально (на самом деле нет) вы пишете. Отказ от классов ради отказа от классов.
0
mayorovp ,  

Потому что нужно решать ту же самую задачу.

0
TheShock ,  
Вот есть код из статьи (дурацкая привычка вставлять код картинками):
Скрытый текст
image


А вот как это пишется на MobX:
class Counter {
  @observable value = 1;
  
  constructor (initialValue) {
    this.value = value;
  }

  @action increment = () => this.value++;
  @action decrement = () => this.value--;
}

@observer
class App extends React.Component {
  render () {
    const { counter } = this.props;

    return (
      <div>
        <p>{counter.value}</p>
        <button onClick={counter.increment}>Increment</button>
        <button onClick={counter.decrement}>Decrement</button>
      </div>
    );
  }
}


И не надо говорить, что стейт должен создаваться во время рендера — это дурацкая идея и ничего общего с «задачей» тут нету.
0
m_vash ,   * (был изменён)
Можете нажать на картинки где много кода, там ссылка на CodeSandBox, наверное Вы правы, нужно вставлять код текстом
0
mayorovp ,   * (был изменён)

На той картинке ничего компоненту App не передается. Откуда в вашем коде взялся counter в props?


Зачем менять внешний контракт компонента делая разные версии не взаимозаменяемыми?


И не надо говорить, что стейт должен создаваться во время рендера — это дурацкая идея и ничего общего с «задачей» тут нету.

Вот этот как раз детали реализации. Где вы в моем коде увидели создание стейта при рендере?

0
TheShock ,  
Где вы в моем коде увидели создание стейта при рендере?

Я увидел это в оригинальном коде, который я процитировал.

Зачем менять внешний контракт компонента делая разные версии не взаимозаменяемыми?

Потому что версия, которая предлагается в статье — не должна существовать.

На той картинке ничего компоненту App не передается. Откуда в вашем коде взялся counter в props?

А у меня — передается. В этом ведь (частично) суть.
0
mayorovp ,   * (был изменён)

Суть — в локальном состоянии для компонента. В старом API React это делалось через this.state/this.setState, в новом — через useState. В mobx-react локальное состояние компонентов делается через свойства с декоратором @observable. Это три механизма — прямые аналоги друг друга, и остаются таковыми даже когда один из них не имеет права существовать.


Или вы в принципе отрицаете возможность компонентов иметь свое состояние?

0
xadd ,  

mobx — это про MVVM. За состояние V отвечает VM.

0
mayorovp ,  

Во-первых, mobx — это не обязательно MVVM. Во-вторых, даже в MVVM вынос абсолютно всей логики вида в вью-модель — антипаттерн.

0
xadd ,  
Не обязательно, но предпочтительно. В примере, очевидно, у нас логика и состояние вью-модели, логику презентации конечно нужно не выносить :)
+1
TheShock ,  
Или вы в принципе отрицаете возможность компонентов иметь свое состояние?

Все, я понял теперь вашу аргументацию.
0
mayorovp ,  

Кстати, самое интересное что в MobX эту ужасную фишку собираются поддерживать. Уже сравнивали — observer на хуках занимает в несколько раз меньше кода чем observer на классах...

0
TheShock ,  
Давайте пруфы, что-ли, почитаем, что там собираются поддерживать.
0
mayorovp ,  
Вот начальное обсуждение: github.com/mobxjs/mobx-react/issues/588
Вот новый репозиторий: github.com/mobxjs/mobx-react-lite
0
TheShock ,  
То есть будут поддерживать не в MobX, а создали отдельную библиотеку для экспериментов с новой фичей. В которой API совершенно отличается от оригинального MobX.
+1
mayorovp ,   * (был изменён)

Ну так как бы и @observer для классов был реализован не в mobx, а в mobx-react! А про сохранение API я ничего и не говорил.

0
TheShock ,  
Окей. Но в чем суть аргумента? Да, MobX собираются поддерживать эту фичу. Это никак не говорит о качестве этой фичи. И никак не говорит об отношении авторов MobX к этой фиче. Даже не говорит, на базе какого кода получается лучше код. На самом деле это ни о чем не говорит кроме того, что MobX, как фреймворк, который, по сути, строится вокруг реакта поддерживает его новые возможности.
+1
mayorovp ,  

Ни в чем. Я сообщил новость, а не приводил аргументы.

+3
faiwer ,  

У меня уже давно закралось подозрение, что все эти pureComputed, React.memo и прочие штуки для производительности — вообще не являются "линией партии" и созданы просто, чтобы не ныли "а чо там медленно всё?". Во всех мануалах, гайдах, оф.документации, да почти везде мы видим примеры кода, в которых при обновлении node на самом верху, мы перестраиваем весь vDomTree целиком вниз, а потом реконсилируем его. И типа нормально, нечего переживать, ведь это же vDom, он же быстрый. о_О. Вот даже в примерах про Hook-и — всё тоже самое. Не удивительно, что они от классов отказываются.

+4
staticlab ,  

Что касается redux'а вообще и dispatch'а в частности, так это то, что евангелисты несли маркетинговую чушь про особый подход (не то, что этот убогий MVC), функциональщину, хотя сам redux — это обычный конечный автомат, state machine, в котором совершенно ООПшным процедурным образом дёргается store.dispatch({}) с передачей обыкновенного сообщения, как это было у Алана Кея и как это давным давно реализовано в WinAPI, только там обработчик — это WindowProc, а здесь — reducer.


Но оказалось, что гениальный redux сам по себе не умеет даже асинхронные вызовы, для чего сначала сам автор придумал костыль redux-thunk, а позже соорудили чуть ли не фреймворк redux-saga, в котором теперь можно легко писать асинхронный код по обработке тех самых сообщений, внезапно, императивным способом.

+3
unel ,  
useState  —  позволяет нам писать чистые функции с доступом к state в них.
useContext  -  позволяет писать в них чистые функции с контекстом.
useRef  —  позволяет писать чистые функции, которые возвращают изменяемый объект ref.

Как функция может быть чистой, если она завязывается не только на пропы (читай — не только на её аргументы)?
Как только мы добавляем внутренний стейт, контекст или какие-то ссылки, уже язык не поворачивается называть функцию чистой.

+4
unel ,  
С того момента как представили React Hooks, сообщество было в восторге от этой фичи

Вот тут вижу частичное укрывание правды. Если сообщество так хорошо их восприняло, то почему тогда Дэн Абрамов начал просветительскую деятельность по поводу хуков в твитере?


Почему вообще не освящён негативный аспект хуков? Почему всё подаётся под соусом "это ещё одна мега-фича которую вы должны начать юзать" и не указываются границы применимости (единственное, что указали — это про производительность в хуках useEffect)