1、调用 store.dispatch(action)
2、Action 会触发给 Store 指定的 root reducer
3、Store 会保存 root reducer 返回的状态树
四、在 react 中注册使用 redux
1、将 Redux 绑定到 React 实例上
(1)、Provider 组件
(2)、Connect() 方法
一、一句话说 redux
redux 就是:先将需要修改的 state 都存入到 store 里,然后发起一个 action 用来描述发生了什么,然后用 reducers 描述 action 如何改变 state 状态树 ,创建store的时候需要传入reducer,真正能改变 store 中数据的是 store.dispatch API。
二、redux 的成员
1、action
Action 是一个单纯的包含
{ type, payload }
的对象,其中:
-
type
是一个常量用来标示动作类型;
-
payload
是这个动作携带的数据。
通常使用函数来生成 action,它最后会返回一个 action 对象。比如:
export function addTodo(text) {
return {
type: ActionTypes.ADD_TODO,
text: text
创建 action 的函数是一个纯函数(pure function)——形如 “(state, action) => state
”,
一个函数的返回结果只依赖其参数,并且执行过程中没有副作用。
Action 需要通过 store.dispatch()
方法来发送。所以现在要触发一个动作只要调用 dispatch
: dispatch(addTodo(text))
。稍后会讲到如何拿到 store.dispatch。
2、reducer
(1)、组件的 reducer
Reducer 用来处理 Action 触发的对状态树的更改。
所以一个 reducer 函数会接受 oldState
和 action
两个参数,返回一个新的 state:(oldState, action) => newState
。
const initialState = {
a: 'a',
b: 'b'
function someApp(state = initialState, action) {
switch (action.type) {
case 'CHANGE_A':
return { ...state, a: 'Modified a' };
case 'CHANGE_B':
return { ...state, b: action.payload };
default:
return state
- 使用 ES6 的扩展语法来确保不会更改到
oldState
而是返回一个 newState。
- 对于不需要处理的 action,直接返回
oldState
对上述代码用“策略模式”进行优化:
const initialState = {
a: 'a',
b: 'b'
function someApp(state = initialState, action) {
var map = {
CHANGE_A: function () {
return { ...state, a: 'Modified a' };
CHANGE_B: function () {
return { ...state, b: action.payload };
if (map[action.type]) {
return map[action.type]();
} else {
return state;
redux 的 Reducer 是一个纯函数,所以绝对不要在 reducer 里面做一些引入 副作用 的事情,比如:
- 直接修改 state 参数对象
- 请求 API
- 调用不纯的函数,比如:
Data.now()
Math.random()
(2)、根 reducer
Redux 里,一个 Store 对应一个 State 状态,所以整个 State 对象就应该由一个总的 reducer 函数管理。但是,如果所有的状态更改逻辑都放在这一个 reducer 里面,显然会变得巨大而难以维护。得益于纯函数的实现,我们只需要让状态树上的每个字段都有一个自己的 reducer 函数来管理,就可以拆分成很小的 reducer 了:
function someApp(state = {}, action) {
return {
a: reducerA(state.a, action),
b: reducerB(state.b, action)
对于 reducerA 和 reducerB 来说,他们依然是形如:(oldState, action) => newState 的函数。只是这时候的 state 不是整个状态树,而是树上的特定字段,每个 reducer 只需要判断 action,管理自己关心的状态字段数据就好了。
Redux 提供了一个工具函数 combineReducers() 来简化这种 reducer 合并:
import { combineReducers } from 'redux';
const someApp = combineReducers({
a: reducerA,
b: reducerB
像 someApp 这种管理整个 State 的 reducer,可以称为根 reducer 即 root reducer。
3、Store
(1)、创建 store
Store 的作用就是连接 Action 和 Reducer,Store 的具体作用如下:
- 保持住整个应用的 State 状态树
- 提供一个
getState()
方法获取 State - 提供一个
dispatch()
方法发送 action 更改 State - 提供一个
subscribe()
方法注册回调函数监听 State 的更改
创建一个 Store 很容易,将 “root reducer” 函数传递给 createStore()
方法即可:
import { createStore } from 'redux';
import someApp from './reducers';
let store = createStore(someApp);
// 你也可以额外指定一个初始 State(initialState),这对于服务端渲染很有用
// let store = createStore(someApp, window.STATE_FROM_SERVER);
现在我们就拿到了 store.dispatch
,可以用来分发 action 了。
(2)、分发 action
// 使用 subscribe 方法创建一个函数,监听 State 的更改
let unsubscribe = store.subscribe(() => console.log(store.getState()));
// 分发 action
store.dispatch({ type: 'CHANGE_A' });
store.dispatch({ type: 'CHANGE_B', payload: 'Modified b' });
// 停止侦听状态更新
unsubscribe();
三、redux 的运作流程
redux 的运作流程如下:store.dispatch(action) -> reducer(state, action) -> store.getState(),这整个过程是一个“单向数据流”。
1、调用 store.dispatch(action)
Action 是一个包含 { type, payload }
的对象,它描述了“发生了什么”,比如:
{ type: 'LIKE_ARTICLE', articleID: 42 }
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
{ type: 'ADD_TODO', text: 'Read the Redux docs.' }
你可以在任何地方调用 store.dispatch(action)
,比如组件内部,Ajax 回调函数里面等等。
2、Action 会触发给 Store 指定的 root reducer
root reducer 会返回一个完整的状态树,State 对象上的各个字段值可以由各自的 reducer 函数处理并返回新的值。
- reducer 函数接受
(state, action)
两个参数 - reducer 函数判断
action.type
然后处理对应的 action.payload
数据来更新并返回一个新的 state
3、Store 会保存 root reducer 返回的状态树
新的 State 会替代旧的 State,然后所有 store.subscribe(listener)
注册的回调函数会被调用,在回调函数里面可以通过 store.getState()
拿到新的 State。
这就是 Redux 的运作流程,接下来看如何在 React 里面使用 Redux。
import {createStore} from 'redux';
function count(state = 0, action) {
switch (action.type) {
case 'ADD':
return state + 1
case 'REDUCER':
return state - 1;
default:
return state
// 创建一个仓库
let store = createStore(count);
let currentValue = store.getState();
console.log('当前的值:', currentValue);
// 定义一个监听的方法
let listener = () => {
const previosValue = currentValue;
currentValue = store.getState();
console.log('上一个值:', previosValue, '当前值:', currentValue)
// 创建一个监听
store.subscribe(listener);
// 分发任务
store.dispatch({type:"ADD"});
store.dispatch({type:"ADD"});
store.dispatch({type:"ADD"});
store.dispatch({type:"REDUCER"});
四、在 react 中注册使用 redux
1、将 Redux 绑定到 React 实例上
react-redux 提供了两个 API:Provider 和 Connect。
(1)、Provider 组件
<Provider> 作为一个容器组件,用来接受 Store,并且让 Store 对子组件可用,用法如下:
import { render } from 'react-dom';
import { Provider } from 'react-redux';
import App from './app';
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
这时候 <Provider> 里面的子组件 <App /> 才可以使用 connect 方法关联 store。
<Provider> 的源码及其实现原理:
import React, { useMemo, useEffect } from 'react'
import PropTypes from 'prop-types'
import { ReactReduxContext } from './Context'
import Subscription from '../utils/Subscription'
function Provider({ store, context, children }) {
const contextValue = useMemo(() => {
const subscription = new Subscription(store)
subscription.onStateChange = subscription.notifyNestedSubs
return {
store,
subscription,
}, [store])
const previousState = useMemo(() => store.getState(), [store])
useEffect(() => {
const { subscription } = contextValue
subscription.trySubscribe()
if (previousState !== store.getState()) {
subscription.notifyNestedSubs()
return () => {
subscription.tryUnsubscribe()
subscription.onStateChange = null
}, [contextValue, previousState])
const Context = context || ReactReduxContext
return <Context.Provider value={contextValue}>{children}</Context.Provider>
if (process.env.NODE_ENV !== 'production') {
Provider.propTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired,
context: PropTypes.object,
children: PropTypes.any,
export default Provider
Provider 利用了 React 一个(暂时)隐藏的特性 Contexts,Context 用来传递一些父容器的属性对所有子孙组件可见,在某些场景下面避免了用 props 传递多层组件的繁琐。
(2)、Connect() 方法
Connect() 方法用来连接 React 组件与 Redux store,不会改变原来的组件类。
Connect() 方法可以接收 4 个可选的参数:connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]):
- [mapStateToProps(state, [ownProps]): stateProps]:该参数是一个函数,用来管理是否允许组件监听 Redux store 的变化。
- 第一个可选参数是一个函数,只有指定了这个参数,这个关联(connected)组件才会监听 Redux Store 的更新,每次更新都会调用 mapStateToProps 这个函数,返回一个字面量对象将会合并到组件的 props 属性。
- 如果你省略了这个 mapStateToProps 参数,你的组件将不会监听 Redux store。
- ownProps 是可选的第二个参数,它是传递给组件的 props,当组件获取到新的 props 时,ownProps 都会拿到这个值并且执行 mapStateToProps 这个函数。
- [mapDispatchProps(dispatch, [ownProps]): dispatchProps]:该参数可以是一个对象,也可以是一个函数,用来指定如何传递 dispatch 给组件。
- 如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中
dispatch
方法会将action creator的返回值作为参数执行。这些属性会被合并到组件的 props 中。 - 如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象,这个对象通过 dispatch 函数与 action creator 以某种方式绑定在一起(提示:你也许会用到 Redux 的辅助函数 bindActionCreators())。
- 如果你省略这个 mapDispatchToProps 参数,默认情况下,dispatch 会注入到你的组件 props 中。
- ownProps 是可选的第二个参数,它是传递给组件的 props,只要组件接收到新 props,mapDispatchToProps 就会被调用。
- [mergeProps(stateProps, dispatchProps, ownProps): props]:该参数是一个函数,用来处理 mapStateToProps 和 mapDispatchProps 返回的结果。
- 如果指定了这个参数,mapStateToProps() 与 mapDispatchToProps() 的执行结果和组件自身的 props 将传入到这个回调函数中。该回调函数返回的对象将作为 props 传递到被包装的组件中。你也许可以用这个回调函数,根据组件的 props 来筛选部分的 state 数据,或者把 props 中的某个特定变量与 action creator 绑定在一起。
- 如果你省略这个参数,默认情况下返回 Object.assign({}, ownProps, stateProps, dispatchProps) 的结果。
- [options]:该参数是一个对象,用来定制 connector 的行为。
- [pure = true] (Boolean): 如果为 true,connector 将执行 shouldComponentUpdate 并且浅对比 mergeProps 的结果,避免不必要的更新,前提是当前组件是一个“纯”组件,它不依赖于任何的输入或 state 而只依赖于 props 和 Redux store 的 state。默认值为 true。
- [withRef = false] (Boolean): 如果为 true,connector 会保存一个对被包装组件实例的引用,该引用通过 getWrappedInstance() 方法获得。默认值为 false。
更多 connect() 方法的参数的使用详见官网:https://www.redux.org.cn/docs/react-redux/api.html
Connect() 方法,会返回另一个函数,这个返回的函数用来接受一个组件类作为参数,最后它会返回一个和 Redux store 关联起来的新组件。
class App extends Component { ... }
export default connect()(App);
这样就可以在 App 这个组件里面通过 props 拿到 Store 的 dispatch 方法。
本文参考:http://caibaojian.com/react/redux-basic.html
它可以自动组合你的 slice reducer,添加你提供的任何 Redux 中间件,redux-thunk默认包含,并启用 Redux DevTools Extension。接受一个动作类型字符串和一个返回承诺的函数,并生成一个pending/fulfilled/rejected基于该承诺分派动作类型的 thunk。接受reducer函数的对象、切片名称和初始状态值,并自动生成切片reducer,并带有相应的actions。,将slice中的reducer可以组成一个对象传入此处;
ReactDOM.render(VDOM,document.getElementById(‘test’)
react:
const VDOM=React.createElement(‘h1’,{id:‘title’},‘Hello,React’)
ReactDOM.render(VDOM,document.getElementById(‘test’)
Redux 是 React 框架下的一款状态管理工具,可以实现多个组件之间的数据共享和传递。学习和掌握 Redux 以及周边生态可以使我们更好的进行 React 项目开发。下面我们就详细的讲述 Redux 在实际项目开发中的使用。
Redux Thunk、Redux Saga等中间件会更复杂一些,它们允许在action中执行异步操作,而不仅仅是在中间件中。这些中间件利用函数包装的方式,允许你在action中编写异步逻辑,然后再dispatch另一个action来更新状态。redux本身是一个同步状态管理库,但他的状态更新过程是通过dispatch一个action来触发的。中间件是Redux的一个扩展机制,他可以在action被dispatch后,到达reducer之前,执行一些额外的逻辑。然而,Redux并不直接支持异步操作。
每每提到Redux都会想起Vuex,相比起Vuex,Redux确实抽象了一些。官方文档给的入门的DEMO中的todoList,其中很多的代码逻辑都得自己去梳理。
容器组件和展示组件
Redux的React绑定库是基于容器组件和展示组件相分离的开发思想。
作用:描述如何展现骨架、样式
直接使用Redux:否
数据来源:props
数据修改:从props调用回调函数
调用...
单一数据源:整个应用中的state被存储在一个store中,即store对象只有一个
state只是可读的:唯一改变state里面值的方式是通过action来触发
使用纯函数来执行修改:reducer是函数,通过执行里面的代码来实现修改
2.redux的基本实现
app.js
import React, { Component } from 'react'