Предисловие 🤔
-
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поставив все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Рабочий процесс примерно такой:- Сначала пользователь передает
store.dispatchметод испускаетaction. - Потом,
storeавтоматический вызовreducersи передать два параметра: текущийstateи получилaction.reducersвернется новыйstate. - Наконец, когда
storeслушалstateизменения, будет вызвана функция прослушивателя, чтобы вызвать повторную визуализацию представления.
- Сначала пользователь передает
- Поставьте картинку для углубления понимания ⚡⚡️⚡️️:
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.
Если есть какие-либо упущения или ошибки в вышеуказанном содержании, пожалуйста, оставьте сообщение ✍️Укажите и продвигайтесь вперед вместе💪💪💪
Если вы считаете, что эта статья полезна для вас, 🏀🏀 оставьте свой драгоценный 👍