Начало работы 📖 и настройка 👩🏾‍💻 Учебник для Redux + React-router

React.js
Начало работы 📖 и настройка 👩🏾‍💻 Учебник для Redux + React-router

Предисловие 🤔

  • Reactэто односторонний поток данных, данные проходят черезpropsПереход от родительского узла к дочернему узлу. Если на высшем уровнеpropsизмененный,ReactВсе дочерние узлы будут перерисованы. Примечание ⚠️:propsдоступен только для чтения (т.е. не может использоватьthis.propsИзменить напрямуюprops), который используется для передачи данных и конфигурации по всему дереву компонентов.
  • Каждый компонент имеет своеstate,stateа такжеpropsРазница в том, чтоstateСуществует только внутри компонентов. Примечание ⚠️: можно вызвать только из текущего компонентаthis.setStateМодификация методаstateзначение (не может быть изменено напрямуюthis.state).
  • Видно, что есть два способа обновить подкомпоненты, один — изменить сам подкомпонентstateзначение, другой — обновить дочерний компонент, полученный от родительского компонента.this.propsзначение для достижения обновления.
  • существуетReactВ процессе разработки проекта большую часть времени нам нужно разрешить компонентам совместно использовать некоторые данные. В общем, мы можем передавать данные между компонентами (черезprops) метод для достижения совместного использования данных, однако, когда данные должны передаваться между компонентами, которые не являются отношениями родитель-потомок, становится очень проблематично работать, и легко уменьшить читабельность кода.В настоящее время нам нужно использоватьstate(статус) инструмент управления.
  • Общие инструменты управления состоянием включаютredux,mobx. из-заreduxПредоставляет всю архитектуру управления состоянием и имеет четкие ограничения, подходящие для использования в приложениях, разрабатываемых большим количеством людей. В этой статье описывается, какReactиспользуется в проектеreduxОсуществлять управление состоянием.

К делу 🥰

  • Этот раздел в основном знакомитreduxа такжеreact-routerСоответствующие базовые знания 📖 и соответствующая конфигурация 👩🏾‍💻.

redux

основная концепция

  • reduxОн подходит для сценариев с несколькими взаимодействиями и несколькими источниками данных. С точки зрения компонентов, если наше приложение имеет следующие сценарии, мы можем рассмотреть возможность его использования в проекте.redux:
    • Состояние компонента, которым необходимо поделиться
    • Состояние должно быть доступно везде
    • Компонент должен изменить глобальное состояние
    • Один компонент должен изменить состояние другого компонента
  • Когда наше приложение соответствует сценариям, упомянутым выше, если мы не используемreduxИли другие инструменты управления состоянием, которые не обрабатывают чтение и запись состояния по определенным правилам, удобочитаемость кода проекта будет сильно снижена, что не способствует повышению эффективности командной разработки.
使用redux与不使用redux的区别示意图
  • Как показано на фиг.reduxпоставив всеstateЦентрализовано в верхней части компонента, можно гибко комбинировать всеstateРаспределите по всем компонентам по мере необходимости.
  • reduxтри принципа:
    • всего приложенияstateхранятся вobject treeв иobject treeсуществует только в единственномstorein (это не означает использованиеreduxнадо поставить всеstateсохранитьredux, компоненты могут по-прежнему поддерживать себяstate).
    • stateдоступен только для чтения.stateизменения, приведет к представлению (view)Изменение. Пользователь не может связатьсяstate, только вид трогает, единственное изменениеstateСпособ состоит в том, чтобы вызвать в представленииaction.actionэто обычный объект, используемый для описания произошедшего события.
    • использоватьreducersвыполнитьstateобновление.reducersэто чистая функция, которая принимаетactionи текущийstateВ качестве аргумента вернуть новый по расчетуstate, чтобы обновить представление.
redux工作流程
  • Как показано на фиг.reduxРабочий процесс примерно такой:
    • Сначала пользователь передаетstore.dispatchметод испускаетaction.
    • Потом,storeавтоматический вызовreducersи передать два параметра: текущийstateи получилaction.reducersвернется новыйstate.
    • Наконец, когдаstoreслушалstateизменения, будет вызвана функция прослушивателя, чтобы вызвать повторную визуализацию представления.
  • Поставьте картинку для углубления понимания ⚡⚡️⚡️️:redux工作过程

API

store
  • storeЕсть место для сохранения данных, у всего приложения может быть только одноstore.
  • reduxпоставкаcreateStoreЭта функция используется для созданияstoreдля хранения всего приложенияstate:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);
  • можно увидеть,createStoreприниматьreducer,исходныйstate(необязательно) и энхансер в качестве аргумента, возвращает новыйstoreобъект.
state
  • storeОбъекты содержат все данные. Если вы хотите получить данные в определенный момент времени, вам нужноstoreСделайте снимок. Сбор данных в этот момент времени называетсяstate.
  • Если вы хотите получить текущий моментstate, в состоянии пройтиstore.getState()способ получения:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);

const state = store.getState();
action
  • stateизменения приведут к изменению представления. Однако пользователи не имеют доступа кstate, касается только представления. так,stateИзменения должны быть инициированы представлением.
  • actionэто уведомление, выдаваемое представлением, уведомлениеstoreВ настоящее времяstateЭто должно было измениться.
  • actionявляется объектом. один из нихtypeобязательный атрибут, указывающий, чтоactionИмя. Другие свойства могут быть установлены свободно, и у сообщества есть спецификация для справки:
const action = {
  type: 'ADD_TODO',
  payload: 'Learn Redux' // 可选属性
};
  • Приведенный выше код определяет имя какADD_TODOизaction, данные, которые он несет,Learn Redux.
Action Creator
  • viewСуществует столько видов сообщений, сколько нужно отправитьaction, если все от руки, то будет очень хлопотно.
  • Функция может быть определена для генерацииaction, эта функция называетсяAction Creator, как в коде нижеaddTodoфункция:
const ADD_TODO = '添加 TODO';

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

const action = addTodo('Learn Redux');
  • redux-actionsэто служебная библиотека, которая позволяет писатьreduxУправление состоянием стало проще. Эта библиотека предоставляетcreateActionМетод используется для создания генератора действий:
import { createAction } from "redux-actions"

export const INCREMENT = 'INCREMENT'
export const increment = createAction(INCREMENT)
  • Приведенный выше код определяет действиеINCREMENT, затем черезcreateActionсоздал соответствующийAction Creator:
    • передачаincrement()вернется, когда{ type: 'INCREMENT' }
    • передачаincrement(10)вернуть{ type: 'INCREMENT', payload: 10 }
store.dispatch()
  • store.dispatch()вид испускаетactionединственный метод, который принимаетactionОбъект как параметр:
import { createStore } from 'redux';
const store = createStore(reducer, [preloadedState], enhancer);

store.dispatch({
  type: 'ADD_TODO',
  payload: 'Learn Redux'
});
  • комбинироватьAction CreatorЭтот код может быть переписан следующим образом:
import { createStore } from 'redux';
import { createAction } from "redux-actions"
const store = createStore(reducer, [preloadedState], enhancer);

const ADD_TODO = 'ADD_TODO';
const add_todo = createAction('ADD_TODO'); // 创建 Action Creator

store.dispatch(add_todo('Learn Redux'));
reducer
  • storeполучатьactionПозднее появился новыйstate, так что представление обновляется.stateПроцесс расчета (обновления) выполняетсяreducerвыполнить.
  • reducerэто функция, которая принимаетactionи текущийstateВ качестве параметра и возвращает новыйstate:
const reducer = function (state, action) {
  // ...
  return new_state;
};
  • Для реализации вызоваstore.dispatchметод выполняется автоматическиreducerфункция, которую необходимо создатьstoreбудетreducerвходящийcreateStoreметод:
import { createStore } from 'redux';
const reducer = function (state, action) {
  // ...
  return new_state;
};
const store = createStore(reducer);
  • В приведенном выше кодеcreateStoreметод принимаетreducerВ качестве аргумента создайте новыйstore. позже всякий раз, когда представление используетstore.dispatchОтправитьstoreновенькийaction, он автоматически вызоветreducerфункция, получать обновленияstate.
  • redux-actionsпри условииhandleActionsспособ обработки несколькихaction:
// 使用方法:
// handleActions(reducerMap, defaultState)

import { handleActions } from 'redux-actions';
const initialState = { 
  counter: 0 
};

const reducer = handleActions(
  {
    INCREMENT: (state, action) => ({
      counter: state.counter + action.payload
    }),
    DECREMENT: (state, action) => ({
      counter: state.counter - action.payload
    })
  },
  initialState,
);

Разделить, объединить редукторы

  • Как упоминалось ранее, вreactВ приложении может быть только одинstoreдля хранения приложенийstate.组件通过调用actionфункция, передающая данные вreducer,reducerОбновите соответствующийstate.
  • Для больших приложенийstateдолжно быть очень большим, поэтомуreducerТакже увеличивается сложность.

расколоть

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

сливаться

  • reduxпри условииcombineReducersВспомогательная функция, преобразующая независимые децентрализованныеreducerобъединены в финалreducerфункцию, а затем создатьstoreтак какcreateStoreпередаются параметры.
  • В соответствии с потребностями бизнеса, мы можем поставить все субreducerПоместите их в разные каталоги, затем единообразно внесите в один файл и, наконец, объедините объединенныеreducerЭкспорт:
// src/model/reducers.ts
import { combineReducers } from 'redux';
import UI from './UI/reducers';
import user from './user/reducers';
import content from './content/reducers';

const rootReducer = combineReducers({
  UI,
  user,
  content,
});

export default rootReducer;

Промежуточное ПО и асинхронные операции

  • правильноreduxГоворя о синхронизации, мы имеем в виду, когда представление испускаетactionпосле,reducerРассчитать немедленноstate(оригиналreduxрабочий процесс), в то время как асинхронный относится кactionПосле того, как он выпущен, он будет выполнен через определенный период времениreducer.
  • Синхронизация обычно происходит в нативномredux, тогда как в большинстве практических сценариев требуется больше асинхронных операций:actionПосле отправки, после вводаreducerАсинхронная задача должна быть выполнена раньше, например, отправкаajaxПолучив данные после запроса, введитеreducerвыполнять расчеты иstateобновить.
  • видимо роднойreduxАсинхронные операции не поддерживаются, что требует нового инструмента — промежуточного ПО (middleware) для работы с данным бизнес-сценарием. По сути, промежуточное ПОstore.dispatchметод был расширен.
  • Промежуточное ПО предоставляется по адресуactionПосле начала, прибывreducerПредыдущая точка расширения: т.е. черезstore.dispatchметод выданactionОн будет проходить через каждое промежуточное программное обеспечение по очереди и, наконец, придёт кreducer.
  • Мы можем использовать промежуточное программное обеспечение для ведения журнала (redux-logger), создавать отчеты о сбоях (писать свои собственныеcrashReporter), вызвать асинхронный интерфейс (redux-saga) или маршрут (connected-react-router) и так далее.
  • reduxобеспечивает роднойapplyMiddlewareметод, его роль заключается в формировании массива всех промежуточных программ и их последовательном выполнении. Если вы хотите использоватьredux-loggerДля реализации функции ведения журнала используется следующее:
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
const logger = createLogger();

const store = createStore(
  reducer,
  applyMiddleware(logger)
);
  • Если имеется несколько промежуточных программ, передайте промежуточные программы последовательно в качестве параметров.applyMiddlewareВ методе:
import { applyMiddleware, createStore } from 'redux';
import createLogger from 'redux-logger';
import createSagaMiddleware from 'redux-saga';

const logger = createLogger(); // 日志记录
const sagaMiddleware = createSagaMiddleware(); // 调用异步接口

let middleware = [sagaMiddleware];
middleware.push(logger);

const store = createStore(
  reducer,
  // 可传initial_state
  applyMiddleware(...middleware)
);
  • Примечание ⚠️:
    • createStoreМетод может принимать начальное состояние всего приложения в качестве параметра (необязательно).Если начальное состояние передается,applyMiddlewareтребуется в качестве третьего параметра.
    • Некоторое промежуточное программное обеспечение имеет требования к порядку, проверьте документацию перед использованием (например,redux-loggerОбязательно поставьте его в конце, иначе вывод будет неверным).

react-redux

Введение концепции

  • описано в предыдущем разделеreduxсам по себе является комбинируемымreact,vue,angularдаже роднойjavaScriptГосударственная библиотека, используемая приложением.
  • чтобыreduxпомогите нам управлятьreactсостояние приложения, вам нужно поставитьreduxа такжеreactподключение, официально предоставлено react-reduxбиблиотека (эта библиотека не является обязательной, или вы можете использовать толькоredux).
  • react-reduxРазделите все компоненты на две категории: компоненты пользовательского интерфейса и компоненты-контейнеры:
    • Компоненты пользовательского интерфейса отвечают только за отрисовку пользовательского интерфейса и не содержат состояние (this.state), все данныеthis.propsпредоставляется без использования каких-либоreduxAPI.
    • Компоненты контейнера отвечают за управление данными и бизнес-логикой, включая состояние (this.state), быть пригодным для использованияreduxAPI.
  • Короче говоря, компонент-контейнер действует как родительский компонент компонента пользовательского интерфейса и отвечает за связь с внешним миром, передачу данных черезpropsПередается компонентам пользовательского интерфейса для визуализации представления.
  • react-reduxОговаривается, что все компоненты пользовательского интерфейса предоставляются пользователем, а компоненты контейнера — пользователем.react-reduxГенерируется автоматически. Другими словами, пользователь отвечает за визуальный слой, а управление состоянием полностью передано ему.react-redux.

API

Подключите метод
  • react-reduxпри условииconnectметод для создания компонентов контейнера из компонентов пользовательского интерфейса:
import { connect } from 'react-redux'

class Dashboard extends React.Component {
    ...
    // 组件内部可以获取 this.props.loading 的值
}

const mapStateToProps = (state) => {
  return {
    loading: state.loading,
  }
}

// 将通过 connect 方法自动生成的容器组件导出
export default connect(
  mapStateToProps, // 可选
  // mapDispatchToProps, // 可选
)(Dashboard)
  • Как видно из приведенного выше кода,connectМетод принимает два необязательных параметра для определения бизнес-логики компонента-контейнера:
    • mapStateToPropsОтвечает за входную логику, собираетсяstateСопоставляется с параметрами, переданными в компоненты пользовательского интерфейса (props)
    • mapDispatchToPropsОтвечает за логику вывода, то есть за отображение пользовательских операций над компонентами пользовательского интерфейса вaction
  • Примечание ⚠️: КогдаconnectКогда метод не передает никаких параметров, сгенерированный компонент-контейнер можно рассматривать только как простую оболочку для компонента пользовательского интерфейса без какой-либо бизнес-логики:
    • пропускатьmapStateToPropsпараметры, компоненты пользовательского интерфейса не будут подписыватьсяstore,Прямо сейчасstoreОбновление компонента пользовательского интерфейса не приводит к обновлению.
    • пропускатьmapDispatchToPropsпараметры, компоненты пользовательского интерфейса не будут рассматривать действия пользователя какactionотправить данные наstore, который нужно вызывать вручную в компонентеstore.dispatchметод.

mapStateToProps

  • mapStateToPropsэто функция, роль которой заключается в созданииstateОбъект (внешний) для компонента пользовательского интерфейсаpropsотображение объектов. Эта функция подписывается на все приложениеstore,в любое времяstateПри его обновлении он будет выполняться автоматически, пересчитывая параметры компонента пользовательского интерфейса, тем самым запуская повторный рендеринг компонента пользовательского интерфейса.
  • mapStateToPropsПервый параметр всегдаstateобъект, вы также можете использовать второй параметр (необязательный), представляющий компонент-контейнерpropsОбъект:
// 容器组件的代码
//    <Dashboard showType="SHOW_ALL">
//      All
//    </Dashboard>
const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.showType === "SHOW_ALL",
    loading: state.loading,
  }
}
  • использоватьownPropsВ качестве параметра, если параметры компонента контейнера изменятся, это также вызовет повторную визуализацию компонента пользовательского интерфейса.

mapDispatchToProps

  • mapDispatchToPropsдаconnectВторой параметр функции, используемый для создания параметров компонента пользовательского интерфейса дляstore.dispatchКарта методов.
  • Поскольку в большинстве проектов используетсяmapDispatchToPropsОн относительно невелик и подробно здесь обсуждаться не будет. оmapStateToProps,mapDispatchToPropsа такжеconnectБолее подробные инструкции по использованию можно найти вДокументация.
Компонент поставщика
  • использоватьconnectПосле того, как метод сгенерирует компонент-контейнер, вам нужно позволить компоненту-контейнеру получитьstateобъект для генерации параметров компонента пользовательского интерфейса.
  • react-reduxпри условииProviderкомпонент, который позволяет компонентам контейнера получатьstate, конкретное использование заключается в использованииProviderКомпонент обертывает корневой компонент проекта (например, приложение), поэтому все подкомпоненты корневого компонента могут быть получены по умолчанию.stateОбъект:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { store } from './store/configureStore';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);

react-router

  • react-routerэто завершеноreactрешение для маршрутизации, оно сохраняетUIа такжеURLсинхронизация. В проекте мы используем последниеv4Версия.
  • Следует отметить ⚠️, что его не следует устанавливать непосредственно во время разработки.react-router, потому что 👉: вv4в версииreact-routerделится на три пакета:react-router,react-router-domа такжеreact-router-native, они различаются следующим образом:
    • react-router: Предоставляет основные компоненты и функции маршрутизации.
    • react-router-dom: Предоставляет компоненты маршрутизации и функции, используемые браузером.
    • react-router-native:поставкаreact-nativeСоответствует компонентам маршрутизации и функциям, используемым платформой.
  • когда нашreactПриложение использует обаreact-routerа такжеredux, они могут быть дополнительно интегрированы для достижения:
    • Будуrouterданные сstoreдля синхронизации и могут быть доступны изstoreдоступrouterданные, доступныеthis.props.dispatchспособ отправкиaction.
    • пройти черезdispatch actionsНавигация, личное понимание доступноstore.dispatch(push('routerName'))Переключайте маршруты.
    • существуетredux devtoolsПоддерживает отладку перемещения во времени для изменений маршрута в файлах .
  • Для достижения вышеуказанных целей вы можетеconnected-react-routerа такжеhistoryДве библиотеки реализованы, и шаги следующие:
    • при созданииstoreфайл для добавления конфигурации, включая созданиеhistoryобъект, использованиеconnected-react-routerкоторый предоставилconnectRouterМетоды иhistoryсоздание объектаroot reducer,использоватьconnected-react-routerкоторый предоставилrouterMiddlewareпромежуточное ПО иhistoryреализация объектаdispatch actionsнавигация.
    import { connectRouter, routerMiddleware } from 'connected-react-router';
    import createHistory from 'history/createBrowserHistory';
    import { createStore, applyMiddleware } from 'redux';
    import { createLogger } from 'redux-logger';
    import createSagaMiddleware from 'redux-saga';
    import reducer from '../model/reducers';
    
    export const history = createHistory();
    
    const sagaMiddleware = createSagaMiddleware(); // 调用异步接口
    let middleware = [sagaMiddleware, routerMiddleware(history)];
    const logger = createLogger(); // 日志记录
    middleware.push(logger);
    
    const initialState = {};
    
    const store = createStore(
      connectRouter(history)(reducer),
      initialState,
      applyMiddleware(...middleware)
    );
    
    • в файле входа проектаindex.jsДобавьте конфигурацию в корневой компонент, в том числе с помощьюconnected-react-routerкоторый предоставилConnectedRouterКомпонент оборачивает маршрут, который будетConnectedRouterкомпонент какProviderподгруппа , и будет вstoreсоздан вhistoryобъект импортируется какpropsпоступающее имуществоConnectedRouterКомпоненты:
    import * as React from 'react';
    import * as ReactDOM from 'react-dom';
    import { Provider } from 'react-redux'
    import { ConnectedRouter } from 'connected-react-router'
    import App from './App'
    import rootSaga from './model/sagas';
    import { store, history } from './store/configureStore';
    
    ReactDOM.render(
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <App />
        </ConnectedRouter>
      </Provider>,
      document.getElementById('root'),
    );
    
  • Вышеупомянутое сделаноreact-routerа такжеreduxГлубокая интеграция ✌️.

Резюме 👀

  • В этой статье описывается, какReactиспользуется в проектеreduxОсуществляется управление состоянием, вводятся соответствующие основы и показывается полный код.
  • Некоторые специальные настройки обычно выполняются в проекте до разработки бизнес-кода, что полезно для последующей разработки проекта.Подробнее см. 👉:Процесс настройки проекта react + typescript.

Если есть какие-либо упущения или ошибки в вышеуказанном содержании, пожалуйста, оставьте сообщение ✍️Укажите и продвигайтесь вперед вместе💪💪💪

Если вы считаете, что эта статья полезна для вас, 🏀🏀 оставьте свой драгоценный 👍

Ссылки 📖

  1. [Перевод] Redux или Mobx, позвольте мне решить вашу путаницу!
  2. Создание приложений React с нуля — Архитектура приложений React
  3. Начало работы с Redux — ПО промежуточного слоя и асинхронные операции
  4. Начало работы с Redux — как использовать React-Redux