Redux
Redux是JavaScript的状态容器,它提供了可预测化的状态管理。
Redux的核心理念:
-
Store
Store是存储着数据的地方,Redux应用只有一个Store。
Redux提供createStore这个函数,用来生成Store。
import {createStore} from 'redux'
////store(创建的时候需要传入一个reducer)
const store = createStore(reducer);
function reducer(state,action){}
-
Action
所有数据的变化,必须通过派发(dispatch)action来更新;
action是一个普通的JavaScript对象,用来描述这次更新的type和content;
const action1 = {
type: "ADD_FRIEND",
info: {
name: "boge",
age: 20
}
}
const action2 = {
type:"CHANGE_NAME",
playload: {
index: 0,
newName: "bogezhenshuai"
}
}
-
Reducer
Reducer是一个纯函数;
Reducer做的事情就是将传入的state和action结合起来生成一个新的state;
//定义initialState是为了给state一个默认值
const initialState = {counter: 0}
function reducer(state = initialState,action){
switch (action.type){
case "INCREMENT":
return {...state,counter:state.counter + 1}
default:
return state;
}
}
Redux的三大原则:
1.单一数据源
- 整个应用程序的state被存储在一颗object tree中,并且这个object tree只存储在一个 store 中;
- Redux并没有强制让我们不能创建多个Store,但是那样做并不利于数据的维护;
- 单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改;
2.State是只读的
- 唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State;
- 这样就确保了View或网络请求都不能直接修改state,它们只能通过action来描述自己想要如何修改state;
- 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心race condition(竟态)的问题;
3.使用纯函数来执行修改
- 通过reducer将 旧state和 actions联系在一起,并且返回一个新的State;
- 随着应用程序的复杂度增加,我们可以将reducer拆分成多个小的reducers,分别操作不同state tree的一部分;
- 但是所有的reducer都应该是纯函数,不能产生任何的副作用;
Redux的使用过程
- 创建一个对象,作为我们要保存的状态:
- 创建Store来存储这个state:
创建store时必须创建reducer;
我们可以通过store.getState来获取当前的state;
- 创建Store来存储这个state:
- 通过action来修改state:
通过dispatch来派发action;
通常actoin中都会有type属性,也可以携带其他的数据;
- 通过action来修改state:
- 修改reducer中的处理代码
- 可以在派发action之前,监听store的变化;
import {createStore} from 'redux'
//定义initialState是为了给state一个默认值
const initialState = {counter: 0}
//action
const action = {type:"INCREMENT"};
function reducer(state = initialState,action){
switch (action.type){
case "INCREMENT":
return {...state,counter:state.counter + 1}
}
}
//store(创建的时候需要传入一个reducer)
const store = createStore(reducer);
//订阅store的修改
store.subscribe(() => {
console.log("counter:",store.getState().counter); //显示counter:1
});
//派发action
store.dispatch(action);
Redux流程.png
react-redux使用
安装react-redux----yarn add react-redux
- 组件home.js文件
import React, { PureComponent } from 'react';
// import {connect} from '../utils/connect';
import {addAction,incAction,changeBannersAction,changeRecommendAction} from '../store/actionCreators';
import {connect} from 'react-redux';
class Home extends PureComponent {
render() {
return (
<div>
<h1>Home</h1>
<h2>当前计数:{this.props.counter}</h2>
<button onClick={e => this.props.increment()}>+1</button>
<button onClick={e => this.props.addNumber(5)}>+5</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
counter: state.counter
}
};
const mapDispatchToProps = dispatch => {
return{
increment: function(){
dispatch(incAction());
},
addNumber: function(num){
dispatch(addAction(num));
}
}
} ;
export default connect(mapStateToProps,mapDispatchToProps)(Home);
- index.js文件
import {Provider} from 'react-redux';
import store from './store';
ReactDOM.render{
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
};
Redux中的异步操作
Redux中进行异步操作需要用到中间件。
官网推荐网络请求的中间件是使用:redux-thunk
redux-thunk是如何做到让我们可以发送异步的请求呢?
- 在默认情况下,在dispatch(action)中,action通常是一个JavaScript的对象;
- redux-thunk可以让dispatch(action函数),action可以是一个函数;
- 该函数会被调用,同时传给这个函数一个dispatch函数和getState函数;
1.dispatch函数用于我们之后再次派发action;
2.getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态;
使用redux-thunk
1. 安装redux-thunk
yarn add redux-thunk
2. 创建store时传入应用middleware的enhance函数
通过applyMiddleware来结合多个Middleware,返回一个enhancer;
将enhancer作为第二个参数传入到createStore中;
- ./store/index.js文件
import {createStore,applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
const enhancer = applyMiddleware(thunkMiddleware);
const store = createStore(reducer,enhancer);
3. 定义返回一个函数的action
注意:这里不是返回一个对象,而是一个函数;
该函数在dispatch之后被执行;
- ./store/actionCreators.js文件
const getHomeMultidataAction = () => {
return (dispatch) => {
axios.get("http://127.207.32.32:8000/home/multidata").then(res => {
const data = res.data.data;
dispatch(changeBannersAction(data.banner.list));
dispatch(changeRecommendsAction(data.recommend.list));
})
}
}
使用redux-saga
redux-saga是另一个比较常用在redux发送异步请求的中间件,它的使用更加的灵活。
1. 安装redux-saga
yarn add redux-saga
2. 集成redux-saga中间件
导入创建中间件的函数;
通过创建中间件的函数,创建中间件,并且放到applyMiddleware函数中;
启动中间件的监听过程,并且传入要监听的saga;
import createSageMiddleware from 'redux-saga';
import {createStore,applyMiddleware} from 'redux';
const sagaMiddleware = createSageMiddleware();
const storeEnhancer = applyMiddleware(thunkMiddleware,sagaMiddleware);
const store = createStore(reducer,enhancer);
//注意:若要运行redux-saga中间件,如下代码
sagaMiddleware.run(saga);
3. saga.js文件的编写
import {takeEvery,put} from 'redux-saga/effects';
import axios from 'axios';
import {
FETCH_HOME_MULTIDATA
} from './home/constants';
import { changeBannersAction, changeRecommendAction } from './home/actionCreators';
function *fetchHomeMultidata(action) {
const res = yield axios.get("http://123.207.32.32:8000/home/multidata");
const banners = res.data.data.banner.list;
const recommend = res.data.data.recommend.list;
yield put(changeBannersAction(banners));
yield put (changeRecommendAction(recommend));
}
function *mySaga(){
//takeLatest takeEvery区别
//takeLatest:依次只能监听一个对应的action
//takeEvery:每一个都会被执行
yield takeEvery(FETCH_HOME_MULTIDATA,fetchHomeMultidata);
}
export default mySaga;
redux-saga常用API
方法 | 解释 |
---|---|
takeEvery | 可以传入多个监听的actionType,每一个都可以被执行 |
takeLatest | 依次只能监听一个action |
put | 在saga中派发action不再通过dispatch,而是用过put |
all | 可以在yield的时候put多个action |