DevGang
Авторизоваться

Как интегрировать React Hooks в ваш проект без изменения кода Redux 

В этом уроке мы рассмотрим, как интегрировать React Hooks в проект React Redux без изменения кода Redux (редукторов и акшенов).

Чтобы сэкономить время, мы можем начать с простого приложения React Redux, а не создавать его с нуля. Это позволит вам видеть код до и после рядом и значительно упростит интеграцию для вашего приложения.

Стартовый код: https://github.com/iqbal125/modern-react-app-sample

Используя правильную версию React

Самое первое, что мы должны сделать, это убедиться, что у нас есть правильная версия React. На момент написания этой статьи create-react-app не дает вам правильную версию. Итак, вы можете использовать create-react-app, затем перейдите в ваш package.json и введите правильную версию. Просто измените React и React-dom на версию 16.8. Сохраните файл и удалите папку с модулями. Запустите npm install и все готово.

{
 "name": "app2",
 "version": "0.1.0",
 "private": true,
 "dependencies": {
   "auth0-js": "^9.8.2",
   "history": "^4.7.2",
   "react": "^16.8.0",
   "react-dom": "^16.8.0",
   "react-redux": "^6.0.0",
   "react-router": "^4.3.1",
   "react-router-dom": "^4.3.1",
   "react-scripts": "2.1.1",
   "redux": "^4.0.1"
 },

Рефакторинг класса React в React Hook

Теперь первое, что мы сделаем, - это рефакторинг компонента класса React в React Hook. Давайте откроем наш файл App.js и превратим его в хук, поэтому рефакторинг вашего App.js будет следующим:

import React, { Component } from 'react';
import Routes from './routes';

const App = () => {   return(
     
React
) } export default App;

Так что в основном просто превратите класс в стрелочную функцию и удалите метод рендеринга. И все, вы создали React Hook!

Настройка другогих хуков

Таким же образом мы можем настроить другие хуки, которые мы настроим в папке с именем Hooks.

Создайте файл hooks_container.js в каталоге hooks и настройте его следующим образом:

import React, { useState } from 'react';

const HooksContainer = () => {
   return(
     
) } export default HooksContainer;

Хук useState()

Теперь мы начнем устанавливать некоторые базовые неглобальные состояния компонентов с помощью хука useState().

Хук useState() похож на функцию React setState(). Это настройка с деструктуризацией массива, где первый элемент в массиве является значением состояния, а второй элемент является функцией для изменения состояния.

Давайте просто создадим базовые кнопки увеличения и уменьшения, чтобы увидеть, как работает функция состояния.

Настройте кнопки так:

import React, { useState } from 'react';
const HooksContainer = () => {

 const [value, setValue] = useState(0)
 const incrementValue = () => {
   setValue(value + 1 )
 }

 const decrementValue = () => {
   setValue(value - 1 )
 }

   return(
     

Local React State: {value}
) } export default HooksContainer;

Обратите внимание, что нам не нужно использовать ключевое слово «props» или «state», где бы мы не могли просто использовать переменную и имя функции напрямую Это одна из вещей, с которой так легко работать с React Hooks.

Ваше приложение должно выглядеть примерно так.

И вы должны иметь возможность свободно увеличивать или уменьшать число.

Теперь, когда у нас есть базовое представление о том, как работает useState(), мы можем перейти к чему-то более сложному.

Хук useReducer()

Теперь мы можем начать настройку хука useReducer().

Прежде чем мы сможем использовать хук useReducer(), мы должны сначала настроить редуктор. Действия на самом деле можно оставить как есть. И изменения, которые мы должны внести в редуктор, очень минимальны. Все, что нам нужно сделать, это изменить операторы экспорта вместо экспорта по умолчанию. Мы должны экспортировать как редуктор, так и исходное состояние.

Чтобы сэкономить время, просто создайте новый редуктор с именем hooks_reducer.js в файле редуктора и скопируйте код из Reducer1. У вас должно быть что-то похожее на это:

import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
 stateprop1: false,
}

export const HooksReducer1 = (state = initialState, action) => {
   switch(action.type) {
     case ACTION_TYPES.SUCCESS:
       return {
         ...state,
         stateprop1: true
       }
     case ACTION_TYPES.FAILURE:
       return {
         ...state,
         stateprop1: false
       }
     default:
       return state
   }
}

Теперь просто импортируйте этот редуктор и его начальное состояние в hooks_container.js. И передайте их обоих в хук useReducer().

import * as HooksReducer1 from '../store/reducers/hooks_reducer';
...
const [state, dispatch] = useReducer(HooksReducer1.HooksReducer1, HooksReducer1.initialState)

Давайте также создадим 2 кнопки, чтобы изменить stateprop1 с false на true, а затем снова на false. И мы также можем создать троичное выражение для отображения текста в зависимости от того, является ли stateprop1 истинным или ложным. Помните, что stateprop1 - это то же самое, что мы настроили в HookReducer1, но мы обновляем здесь в нашем контейнере.

И мы используем те же самые ранее существующие действия для обновления редуктора. Заметьте, в комментариях я оставил два альтернативных метода диспетчеризации действий. Они все делают одно и то же. Возврат объекта javascript с типом строки SUCCESS.

Итак, ваш код должен выглядеть так:

import React, { useState } from 'react';
import * as ACTIONS from '../store/actions/actions';
import * as HooksReducer1 from '../store/hooks_state/reducer1_hooks';

const HooksContainer = () => {
 const [state, dispatch] = useReducer(HooksReducer1.HooksReducer1, HooksReducer1.initialState)
 const [value, setValue] = useState(0)

 const incrementValue = () => {
   setValue(value + 1 )
 }

 const decrementValue = () => {
   setValue(value - 1 )
 }
 
 const handleDispatchTrue = () => {
   //    dispatch(type: "SUCCESS")
   //    dispatch(ACTIONS.SUCCESS)
   dispatch(ACTIONS.success())
 }

 const handleDispatchFalse = () => {
   //     dispatch(type: "FAILURE")
   //    dispatch(ACTIONS.FAILURE)
   dispatch(ACTIONS.failure())
 }

   return(
     


Local React State: {value}
{state.stateprop1 ?

stateprop1 is true

:

stateprop1 is false

}
) } export default HooksContainer;

Ваше приложение должно выглядеть так, и вы сможете изменить stateprop1 из контейнера hooks:

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

Настройка Actions и Reducer

Прежде чем мы настроим Context, давайте настроим Actions и Reducer, которые мы будем использовать с ним. Итак, давайте добавим второе свойство к HooksReducer1 с именем stateprop2 и установим его значение в 0.

Теперь нам нужно настроить действия и типы действий для работы с этим новым элементом состояния.

Сначала давайте создадим 2 типа действий для stateprop2:

export const INC_GLOBAL_STATE = "INC_GLOBAL_STATE"
export const DEC_GLOBAL_STATE = "DEC_GLOBAL_STATE"

Затем мы можем зайти в наш файл действий и создать 2 экшена для обработки этих типов действий.

export const inc_global_state = () => {
 return {
 type: ACTION_TYPES.INC_GLOBAL_STATE
 }
}

export const dec_global_state = () => {
 return {
 type: ACTION_TYPES.DEC_GLOBAL_STATE
 }
}

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

import * as ACTION_TYPES from '../actions/action_types'

export const initialState = {
 stateprop1: false,
 stateprop2: 0
}

export const HooksReducer1 = (state = initialState, action) => {
   switch(action.type) {
     case ACTION_TYPES.SUCCESS:
       return {
         ...state,
         stateprop1: true
       }
     case ACTION_TYPES.FAILURE:
       return {
         ...state,
         stateprop1: false
       }
     case ACTION_TYPES.INC_GLOBAL_STATE:
       return {
         ...state,
         stateprop2: state.stateprop2 + 1
       }
     case ACTION_TYPES.DEC_GLOBAL_STATE:
       return {
         ...state,
         stateprop2: state.stateprop2 - 1 
       }
     default:
       return state
   }
}

React Context

Далее нам нужно настроить объект контекста. Просто создайте другой файл context.js и настройте его так:

import React from 'react';

const Context = React.createContext({
 prop1: false
})
export default Context;

Обратите внимание, что prop1 здесь не имеет значения. Мы переопределим это в нашем файле App.js. Мы просто предоставили prop1 для инициализации объекта Context. Весь код для обновления и чтения нашего состояния будет сделан в файле App.js.

Теперь давайте импортируем этот объект контекста в наш файл App.js. Также импортируйте HooksReducer1 и Actions, так как мы будем использовать их здесь.

Давайте также настроим useReducer так же, как и раньше.

import React, { useReducer } from 'react';
import Routes from './routes';
import Context from './utils/context';
import * as ACTIONS from './store/actions/actions';
import * as HooksReducer1 from './store/reducers/hooks_reducer';

const App = () => {
 const [valueGlobal, dispatchActionsGlobal] = useReducer(HooksReducer1.HooksReducer1, HooksReducer1.initialState)
...

Затем нам нужно создать 2 функции для отправки создателям наших действий, которые мы только что создали. Эти функции будут увеличивать и уменьшать stateprop2.

Также нам нужно обернуть наши маршруты компонентом < Context.Provider />. Это то, что позволяет нам иметь глобальное состояние. Компонент < Context.Provider /> передает все состояние дочерним компонентам. Поскольку App.js является корневым компонентом, состояние передается каждому компоненту приложения, что делает его глобальным.

Само состояние содержится в prop, называемой «value». Все это похоже на компонент < Provider /> и prop «store», которые можно увидеть в React-Redux.

Затем нам нужно передать диспетчер состояния и действия как свойства в значение prop. Здесь нам понадобятся 3 свойства: одно для функции, увеличивающей значение нашего состояния, одно для функции, уменьшающей значение нашего состояния, и одно для хранения фактического значения состояния.

import React, { useReducer } from 'react';
import Routes from './routes';
import Context from './utils/context';
import * as ACTIONS from './store/actions/actions';
import * as HooksReducer1 from './store/reducers/hooks_reducer';

const App = () => {
 const [valueGlobal, dispatchActionsGlobal] = useReducer(HooksReducer1.HooksReducer1, HooksReducer1.initialState)

   const incrementGlobalValue = () => {
     dispatchActionsGlobal(ACTIONS.inc_global_state() )
   }

   const decrementGlobalValue = () => {
     dispatchActionsGlobal(ACTIONS.dec_global_state() )
   }

   return(
     
React incrementGlobalValue(), decGlobalValue: () => decrementGlobalValue() }}>
) } export default App;

Я намеренно сохранил все имена функций и свойств разными, чтобы было легче увидеть, откуда все идет, когда мы используем Context в дочернем компоненте.

Итак, теперь все эти свойства, определенные в значении prop, могут быть доступны для всех дочерних компонентов, и поэтому мы имеем глобальное состояние!

Использование контекста в дочернем компоненте с хуком useContext().

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

Чтобы использовать Context в нашем контейнере хуков, нам сначала нужно его импортировать и передать весь объект Context в хуки useContext. Вот так:

...

import Context from '../utils/context';

const HooksContainer = () => {
 const context = useContext(Context)
 
...

Затем мы можем напрямую получить доступ к свойствам, которые мы установили в значении prop напрямую через контекстную переменную.

...




...

Помните, что addGlobalValue() - это имя свойства, которое мы передали значению prop в App.js. Это не имя функции для отправки действий или имя функции, которую мы установили в хуке useReducer() в App.js.

Доступ к значению состояния через Context осуществляется следующим образом:

...

Global Value: {context.valueGlobalState.stateprop2}

...

Как и в случае с действиями диспетчера, valueGlobalState - это имя свойства, предоставляемое значению prop. И мы должны получить доступ к stateprop2 с точечной нотацией из свойства valueGlobalState, поскольку valueGlobalState содержит весь intialState из HooksReducer1, включая stateprop1.

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

Вы можете использовать этот шаблон, чтобы существенно увеличить его для всего кода Redux.

Окончательный код: https://github.com/iqbal125/react-hooks-basic

Резюме

Итак, вот концептуальное резюме того, как это сделать (требует базовых знаний React Hooks):

Actions не нужно менять вообще. Reducers также не нужно менять. Просто экспортируйте как исходное состояние, так и редуктор вместо одного редуктора. Не используйте «экспорт по умолчанию» внизу файла редуктора.

Импортируйте редуктор и его начальное состояние в корневой файл App.js. Вызовите хук useReducer() в корневом файле App.js и сохраните его в переменной. Подобно хуку useState, первый элемент в деструктуризации массива - это значение состояния, а второй элемент - это функция для изменения состояния. Затем передайте оба параметра Reducer и InitialState, которые вы импортировали, в хук useReducer(). Импортируйте столько редукторов, сколько хотите, и передайте каждый из них в отдельный хук useReducer().

Импортируйте действия в App.js как обычно. Диспатч акшены тоже точно такие же. Вместо использования функции mapDispatchToProps() вы будете отправлять действия из функции изменения состояния (второй элемент в деструктуризации массива) из вызова ловушки useReducer().

Установите и инициализируйте функцию React.CreateContext () в другом файле и импортируйте ее в App.js. Затем оберните ваш < Routes /> с помощью < Context.Provider >. Как правило, вам потребуется 3 свойства для каждого элемента состояния для значения prop в поставщике. 1 свойство для установки состояния в новое значение, 1 для перехода в фактическое состояние и 1 для возврата состояния в состояние по умолчанию.

Затем, чтобы использовать состояние в компонентах, вы сначала импортируете объект контекста из context.js, а затем просто передаете его в ловушку useContext() и сохраняете его в переменной с именем «context» или как вам нравится. Затем, чтобы получить доступ к свойству состояния, просто введите имя переменной «context» «.», Затем имя свойства, заданное в значении prop, а затем имя свойства, заданное в initialState редуктора. Для отправки действий просто сделайте «context» «.», Затем назовите имя свойства.

Как только это будет сделано, ваше контекстное состояние будет доступно глобально и будет работать с вашим существующим кодом React Redux.

Перевод статьи: How to integrate React Hooks into your project without changing your Redux code

#React
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу