话不多说,先上图
依上图所示,简单分析redux工作流程。
Store根据Reducer返回的state创建公共state仓库,组件中引入Store即可用store.getState()获取公共state;
如果组件中想要改变公共state可以用store.dispatch(action)派发出一个动作通知Store,接到通知后的Store将这个动作以及原来的公共state传给Reducer,然后Reducer经过处理返回新的公共state给Store,Store再将Reducer新返回的state替换原来的state,这时在组件中通过store.subscribe(处理函数)监听公共state变化。
没错,听起来确实有点绕,但动手敲一遍思路就清晰多了。其实跟vuex思路差不多。
下面是基本使用
安装redux
npm install redux --save
在src目录下创建store目录,store目录下创建index.js和reducer.js两个文件。
store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
// 第二个参数用来配置chrome中的redux扩展
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
export default store;
这里需要注意的是createStore函数的第二个参数是为了chrome浏览器的redux扩展,先要翻墙安装redux devtools。
store/reducer.js
// 深度克隆对象函数
function deepCopy(obj){
//如果是非对象直接返回,包括function(特殊)
if(typeof obj !== 'object') return obj;
//如果是null
if(obj === null) return null;
//如果是日期对象
if(obj instanceof Date) return new Date(obj);
//如果是正则对象
if(obj instanceof RegExp) return new RegExp(obj);
//不直接定义[]或{}是为了要保留该对象的原型链
var tempObj = new obj.constructor();
for(var key in obj){
tempObj[key] = typeof obj[key] == 'object' ? deepCopy(obj[key]) : obj[key];
}
return tempObj;
}
const defaultState = {
list: [1,2,3]
}
export default (state = defaultState, action) => {
if(action.type === 'add_list_item'){
let newState = deepCopy(state); //reducer不能直接改变state,需要复制出来一份
newState.list.push(action.value);
return newState;
}
return state;
}
这里需要注意的是Reducer中不要直接更改state,可以先复制出一份副本,修改副本,然后将副本返回给Store。
组件代码
import React, {Component} from 'react';
import 'antd/dist/antd.css';
import {Input, Button, List} from 'antd';
import store from './store';
class TodoList extends Component{
constructor(props){
super(props);
this.state = {
inputValue: '',
list: store.getState().list //公共state中的list
}
// 监听store变化
store.subscribe(this.handleStoreChange);
}
render(){
console.log(this.state)
return (
<div>
<Input
placeholder="请输入"
style={{width:'300px',marginRight:'10px'}}
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.addListItem}>提交</Button>
<List
style={{width:'300px', marginTop:'10px'}}
bordered
dataSource={this.state.list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
handleInputChange = e => {
this.setState({inputValue: e.target.value})
}
addListItem = () => {
// 派发action
store.dispatch({
type: 'add_list_item',
value: this.state.inputValue
})
}
handleStoreChange = () => { // 当store发生改变时做的处理,store.subscribe中添加
this.setState({list: store.getState().list});
}
}
export default TodoList;
这里组件中引入了antd布局,而且我直接使用了箭头函数在类中定义函数,这是es7语法,还是推荐在constructor中绑定this。
效果图
redux devtools
在实际应用中,我们也应该将action统一规范管理,我们可以在store目录下再新建actionTypes.js和actionCreator.js两个文件。
actionTypes.js规范action.type
//添加列表项的action.type
export const ADD_LIST_ITEM = 'add_list_item';
actionCreator.js统一action
import {ADD_LIST_ITEM} from './actionTypes';
// 返回添加列表项的action
export let AddListItemAction = value => ({
type: ADD_LIST_ITEM,
value
})
然后在组件中引入使用即可。