1. Redux的作用
- 无论是组件定义自己的state,还是组件之间的通信通过props进行传递;也包括通过Context进行数据之间的共享;
-
React主要负责帮助我们管理视图,state如何维护最终还是我们自己来决定;
2. Redux核心理念 - Store

3. Redux核心理念 - action
- 所有数据的变化,必须通过派发(dispatch)action来更新;
-
action是一个普通的JavaScript对象,用来描述这次更新的type和content;

- 强制使用action的好处是可以清晰的知道数据到底发生了什么样的变化,所有的数据变化都是可跟追、可预测的;
- actions可以派发的是对象或者函数;
4. Redux核心理念 - reducer
通过actions派发修改state状态的桥梁,就是reducer
- reducer是一个纯函数;
-
reducer做的事情就是将传入的state和action结合起来生成一个新的state;
import * as actionsTypes from './constant'
const initialState = {
counter: 10
}
function reducer(state = initialState, actions) {
switch(actions.type) {
case actionsTypes.ADD_NUMBER:
return {...state, counter: state.counter + actions.num}
case actionsTypes.SUB_NUMBER:
return {...state, counter: state.counter - actions.num}
default:
return state
}
}
export default reducer
5. Redux三大原则
单一数据源:
- 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中;
-
Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;
-
单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;
State是只读的:
- 唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State;
-
这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;
-
可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;
- 通过reducer将 旧state和 actions联系在一起,并且返回一个新的State;
-
随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;
-
所有的reducer都应该是纯函数,不能产生任何的副作用;
6. Redux使用过程
1. 创建一个对象,作为我们要保存的状态;
2. 创建Store来存储这个state;
- 创建store时必须创建reducer;
-
可以通过 store.getState 来获取当前的state;
- 通过dispatch来派发action;
-
通常action中都会有type属性,也可以携带其他的数据;
- reducer是一个纯函数,不需要直接修改state;
5. Redux结构划分;

constant.js 定义type的常量
export const ADD_NUMBER = "add_number"
export const SUB_NUMBER = "sub_number"
createActions.js dispatch派发的actions, 返回一个对象
import * as actionsType from './constant'
export const addNumberActions = (num) => ({
type: actionsType.ADD_NUMBER,
num
})
export const subNumberActions = (num) => ({
type: actionsType.SUB_NUMBER,
num
})
reducer.js 处理派发的actions,返回新的state 实现state状态更新
import * as actionsTypes from './constant'
const initialState = {
counter: 10
}
function reducer(state = initialState, actions) {
switch(actions.type) {
case actionsTypes.ADD_NUMBER:
return {...state, counter: state.counter + actions.num}
case actionsTypes.SUB_NUMBER:
return {...state, counter: state.counter - actions.num}
default:
return state
}
}
export default reducer
index.js 导出reducer
import reducer from "./reducer";
export default reducer
export * from "./createActions"
创建store
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import counterReducer from './features/counter'
import homeReducer from './features/home'
import userInfoReducer from './features/userInfo'
const reducer = combineReducers({
counter: counterReducer,
home: homeReducer,
userInfo: userInfoReducer
})
// 打开 redux-devtools {trace: true} 追踪代码执行
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
export default store
7. Redux使用流程
注意:此图出自coderwhy老师的react课程讲解,有兴趣的小伙伴可以腾讯课堂搜索why老师课程学习
Redux官方文档对于Redux更新state的流程图表述:
8. Redux融入react使用
- 在 componentDidMount 中定义数据的变化,当数据发生变化时重新设置 counter;
- 在发生点击事件时,调用store的dispatch来派发对应的action;
8.1 react-redux使用
前文开头强调redux和react没有直接的关系,在React, Angular, Ember, jQuery, or vanilla JavaScript也完全可以使用Redux。
React官方为了在项目中更高效的使用redux,redux官方提供了react-redux库
npm install react-redux
import { Provider } from 'react-redux'
import store from "./store"
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(
<Provider store={store}>
<App/>
</Provider>
)
组件中:
import React, { PureComponent } from 'react'
import { connect } from 'react-redux'
import { addNumberActions, subNumberActions } from '../../store/features/counter'
export class About extends PureComponent {
calcNumber(num, isAdd) {
if(isAdd) {
this.props.addNumber(num)
} else {
this.props.subNumber(num)
}
}
render() {
const { counter, banners, recommends } = this.props
return (
<div>
<h2>About: counter: {counter}</h2>
<div>
<button onClick={e =>this.calcNumber(5, true)}>+5</button>
<button onClick={e =>this.calcNumber(5, false)}>-5</button>
</div>
<div>
<h2>banners数据:</h2>
<ul>
{
banners.map((item, index) => {
return <li key={index}>{item.title}</li>
})
}
</ul>
</div>
<div>
<h2>recommends数据:</h2>
<ul>
{
recommends.map((item, index) => {
return <li key={index}>{item.title}</li>
})
}
</ul>
</div>
</div>
)
}
}
const mapStateToProps = (state) => ({
counter: state.counter.counter,
banners: state.home.banners,
recommends: state.home.recommends
})
const mapDispatchToProps = (dispatch) => ({
addNumber(num) {
dispatch(addNumberActions(num))
},
subNumber(num) {
dispatch(subNumberActions(num))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(About)
8.2 组件中的异步操作
对于网络请求中的数据,需要交给redux来处理,需要通过中间件(middleware)来实现
理解中间件:
- 中间件的目的是在dispatch的action和最终达到的reducer之间,扩展一些自己的代码;
-
比如日志记录、调用异步接口、添加代码调试功能等等;
- 默认情况下的dispatch(action),action需要是一个JavaScript的对象;
- redux-thunk可以让dispatch(action函数),action可以是一个函数;
-
该函数会被调用,并且会传给这个函数一个dispatch函数和getState函数;
-
dispatch函数用于我们之后再次派发action;
-
getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态;
-
npm install redux-thunk
const store = createStore(reducer, applyMiddleware(thunk))
export const fetchHomeMultidataActions = () => {
return (dispatch, getData) => {
axios.get('http://123.207.32.32:8000/home/multidata').then(res => {
const banners = res.data.data.banner.list
const recommends = res.data.data.recommend.list
dispatch(changeBannersActions(banners))
dispatch(changeRecommendsActions(recommends))
})
}
}
8.3 store的模块化分
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
const reducer = combineReducers({
counter: counterReducer,
home: homeReducer,
userInfo: userInfoReducer
})
- 将我们传入的reducers合并到一个对象中,最终返回一个combination的函数(相当于我们之前的reducer函数了);
-
在执行combination函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state;
-
新的state会触发订阅者发生对应的刷新,而旧的state可以有效的组织订阅者发生刷新;