Redux 是JavaScript的状态管理器.由Facebook的Flux演变而来,
Redux是一个可预测的状态管理容器,实质上也是Flux里面的单向数据流
的思想.
Redux是第三方的JavaScript库,在需要状态管理的地方,都可以使用,通常我们都是搭配React进行使用,Redux的存在仅仅是为了状态管理.
整个应用的 state被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中
可以将Store理解为全局的一个变量,并且全局只有一个Store
,开发过程中,可以将state保存在内存中,从而加快开发速度
唯一改变state的方法就是触发action,action是一个用于描述已发生事件的普通对象.
action
就是一个普通的对象,改变state,只能通过触发action才能完成.这样确保View和网络请求都无法直接修改状态,只是为了表达一个动作,一个修改state的意图.Store中有个dispatch
,dispatch接收Action参数,然后通过Store.dispatch()
触发,来改变Store中的state.
Store.dispatch({
type:"EDIT_STATE",//Action的一个类型名称
payload //表示Action携带的状态,也就是数据,需要发生的行为
});
为了描述action如何改变state tree,你需要编写reducers
纯函数:一个函数的返回结果只能依赖于它的参数,相同的输入,永远都会得到相同的输出,而且没有任何副作用
纯函数是函数式变成的概念,必须遵守以下约束:
所以,我们改变state,需要写reducers,而reducers是纯函数,就可以保证同样的State,必定得到相同的View,所以Reducer函数里面不能改变state,我们必须返回一个全新的对象.所以,如果State是对象的话,必须进行深复制操作!
Redux由三部分组成:Action,Reducer,Store
Action用来表达动作或者信号
Reducer根据Action更新State
Store是存放所有的状态容器
在Redux中,Action的概念,使用方法和创建方法与Flux基本一致,是一个用于描述发生了什么事情的JavaScript对象.也是信息的载体,Action是一个能够把state从应用传到Store的载体.
// 提供一个action信息
let action= {
type: "TODO_ADD", //Action名称
payload //Action的数据行为
}
PS:Action是Store中唯一的来源
state
的变化,会导致view
的变化,所以,State
的变化必须是View
导致的.Action就是View发出的通知,表示State应该要发生变化了.
Action是一个对象,type属性是必须的,并且还是一个字符串常量.表示Action要执行的动作名称,其他属性可以自由设置
在大型项目中,我们一般使用模块或文件单独存放actionType
:
//actionType.js
//创建actionTypes字典
export const TODO_ADD="TODO_ADD";
export const TODO_UPDATE="TODO_UPDATE";
export const TODO_DELETE="TODO_DELETE";
export const TODO_CLEAR="TODO_CLEAR";
//菜单字典
export const MENU_QUERY="MENU_QUERY";
使用时,在Action或Reducers中引入即可
import { TODO_ADD,TODO_UPDATE,MENU_QUERY } from '../actionTypes';
实际开发中,一个模块的功能肯定不止一个Action,难道我们每次都要定义一个Action的动作变量吗?显然不是的,我们需要生成Action的实例的动作文件
// 添加
export function addTodo(payload) {
return {
type: TODO_ADD,
payload
}
}
//修改
export function updateTodo(payload) {
return {
type: TODO_Edit,
payload
}
}
在 Redux 中的 action 创建函数只是简单的返回一个 action,这就是Action的创建
Action定义了要执行的动作,但是没有说如何更改state,所以Reducer的职责就是定义整个应用的state如何更改.
Reducer的作用就是根据Action执行去更新Store中的状态
在Redux中,所有的状态都被保存在一个单一的object tree中
,与Flux并不一样,Flux可以有多个Store来存储各种不同的状态.
前面说了,Reducer是一个纯函数,它接受之前的state和Action对象,并返回新的state,并且纯函数不会有副作用,只要传入的参数确定,返回的结果总数唯一的.
// 初始化state,也就是state的初始状态
let initState = {
msg: "hello world",
list: [{
id: 1, task: "吃饭"
}, {
id: 2, task: "睡觉"
}]
}
// 1.state不能直接修改,也就是任何时候,state都没有被改变
// 2.state的改变不是原来的state,而是我们深拷贝了一个新的state,去替换旧的
// 3.reducer 这个函数里抛出去的是深拷贝的state
export default function reducer(state = initState, action) {
let newState = JSON.parse(JSON.stringify(state));
switch (action.type) {
case TODO_ADD:
newState.list.push(action.payload);
break;
case TODO_DELETE:
newState.list.filter((ele) => {
return ele.id != action.payload;
});
default:
break;
}
return newState;
}
PS:
1.state不能直接修改,也就是任何时候,state都没有被改变
state的改变不是原来的state,而是我们深拷贝了一个新的state,去替换旧的
3.reducer 这个函数里抛出去的是深拷贝的state
深浅拷贝详解,以及使用方式
一个应用显然不会只有那么一个Reducers,所以我们需要将Reducers进行拆解并且合并.也就是说,可以把Reducer拆解成多个单独的函数.而这单独的函数负责自己独立的state.更有利于管理好自己的代码,然后将这所有单独的函数进行合并.
combineReducers(Object)只接收一个对象参数
//引入合并Reducers的函数方法
import { createStore,combineReducers} from 'redux';
//Reducers单元模块
import todoReducers from './reducers/todo';
import menuReducers from './reducers/menu';
//合并 Reducers
const reducers = combineReducers({
//key value形式,后面Store取值时,取key即可
todo: todoReducers,
menu: menuReducers
})
export default createStore (reducers);
Redux中的Store概念和Flux中的Store基本相同,但是Redux中全局只有一个Store,用于存储整个应用的状态
强调:Redux应用只有一个单一的Store
,当需要拆分数据处理逻辑时,使用Reducer组合方式,不是创建多个Store
Store的职责:
dispatch(action) 执行一个Action,来更新state
Store 是通过Redux提供的createStore来创建的
//引入创建Store的函数和合并Reducers的函数方法
import { createStore, combineReducers } from 'redux';
//Reducers单元模块
import todoReducers from './reducers/todo';
import menuReducers from './reducers/menu';
//合并 Reducers
const reducers = combineReducers({
todo: todoReducers,
menu: menuReducers
})
//创建Store
const Store = createStore(reducers);
//抛出
export default Store;
createStore接收一个功能是reducer的函数作为参数
Reducer
接收2个参数,一个是当前的State和要执行的Action.返回新生成的Store对象.createStore还有另外几个参数,有着各自的功能,我这里只介绍第一个参数.后面还有接收一个中间件的参数.
Redux是不依赖于React存在的,它本身能支持React,Angular 和jQuery等.要让Redux在React上使用,需要二者建立连接,于是就有了react-redux
react-redux是基于容器组件和展示组件相互分离的开发思想
安装 react-redux
npm install react-redux -S
react-redux提供了2个重要的对象 Provider
和connect
使用react-redux时,先要在最顶层创建一个Provider的组件,用于将所有的React组件包裹起来,从而使React的所有组件都成为Provider的子组件.然后将创建好的Store,作为Provider的属性传递下去
import { Provider } from 'react-redux';
import store from '@/store'
// console.log(store);
export default class App extends React.Component {
render() {
return (
<HashRouter>
<div className="app" >
<Provider store={store}>
<LoyoutCom></LoyoutCom>
</Provider>
</div>
</HashRouter>
);
}
}
connect的主要作用是连接React组件与React Store.当前组件可以通过props获取应用中的state和Action
connect接收4个参数:
我们项目经常使用的是前两种:mapStateToProps()
和mapDispatchToProps()
主要作用是将Store中的数据作为props,绑定到当前组件
//将state中的数据变成当前组件的props
function mapStateToProps(state) {
return {
//从Store中获取需要的state
menuList: state.menu.menuList
}
}
//取值时可以直接 this.props.menuList
主要作用是将当前的Action作为Props绑定到当前组件
//将Action动作转为当前组件的props
function mapDispatchToProps(dispatch) {
return {
//派发一个Action,需要执行
getMenuList: (params) => dispatch(queryMenu(params))
}
}
connect属于高阶组件,它允许向一个现有组件添加新功能,同时不改变其结构
export default connect(mapStateToProps, mapActionToProps)(LoyoutCom);
上面所讲的Action是发起同步的.但是如果我们每次调用接口,就需要发起异步的Action,这里就需要我们的中间件middleware了.
redux默认只支持同步的action,要想支持异步的action的行为,需要使用第三方中间件,一般使用redux-thunk
中间件的作用是把异步行为(比如调接口)的action转为3个同步的action
,以此来解决redux只支持同步action
的特点
安装redux-thunk
npm install redux-thunk -S
应用中间件
//引入创建Store的函数和合并Reducers的函数方法
import { createStore, combineReducers, applyMiddleware } from 'redux';
//Reducers单元模块
import todoReducers from './reducers/todo';
import menuReducers from './reducers/menu';
//引入中间件,作用是把异步行为(比如调接口)的action转为3个同步的action
import thunk from 'redux-thunk';
//合并 Reducers
const reducers = combineReducers({
todo: todoReducers,
menu: menuReducers
})
export default createStore(reducers, applyMiddleware(thunk));
applyMiddleware
是createStore的第二个参数,告诉createStore如何处理中间件
创建异步的action与同步的不太一样,需要dispatch 2个异步的action
import { MENU_QUERY } from '../actionTypes';
import { menuapi } from '@/utils'
//异步获取菜单的Action
export function queryMenu(params) {
return function (dispatch) {
menuapi.getMenuList(params).then(res => {
//查询成功的Action
dispatch({
type: MENU_QUERY,
payload: res.list
});
}).catch((err) => {
//失败的Action
dispatch({
type: MENU_QUERY,
payload: ''
});
});
};
}
一个成功的action,一个失败的action
发起action异步行为也稍微有点出入:
//将Action动作转为当前组件的props
function mapDispatchToProps(dispatch) {
return {
getMenuList: (params) => dispatch(queryMenu(params))
}
}
然后要执行当前的接口的话,需要在某个特定的时候触发,比如componentDidMount,或者事件.
插上一张阮一峰大神写的相关Redux的工作流程图
解读步骤如下: