爱生活,爱编程,学习使我快乐
Facebook:react在2013年开源的时候,还开源了flux(已经过时),flux是官方推出最原始的辅助react的数据层框架,但实际使用时有缺点:公共数据存储区域store由很多的store所组成,这样会导致数据存储的时候会有数据依赖的问题。便在flux的基础上引入了reducer的概念–>redux。
redux = reducer+flux
redux基础设计理念: 数据放在Store,公用存储空间。组件改变Store 的数据,其他组件就能感知到Store里数据的变化,取到新的数据,从而间接的实现了组件之间实现数据传递的功能
三大设计原则:
用一个实际的例子来解释redux的工作流程
coponent是借阅人
store是用来存储数据的. 相当于图书管理员
action就是借阅人对图书管理员说的话
reducers就是借阅记录
当借阅人(component)要借书时, 对管理员(store)说”我要借xx书”(action), 管理员就在借阅记录(reducers)上找出来。然后,管理员将书(state)给借阅人(component)。
使用 npm 或 yarn 安装
npm install antd --save
yarn add antd
具体代码
import React, { Component } from 'React';
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
import { Input, Button, List } from 'antd';
const data = [
'AAAAA',
'BBBBB',
'CCCCC',
'DDDDD',
'EEEEE',
];
class TodlList extends Component {
render() {
return() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input placeholder='todo info' style={{width: '300px'}} />
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
}
npm install --save redux
// 设置一个默认值,如果state没有值则使用这个默认值
const defaultState = {
inputValue:'123',
list: [1, 2]
}
// 导出一个函数(第一个参数是之前的state,第二个值数是一个action对象,包括类型和传值)
export default (state = defaultState, action) => {
return state;
}
// 引入createStore方法
import { createStore } from 'redux';
// 引入reducer
import reducer from './reducer';
// 创建store,并把reducer中导出的方法传进来
const store = createStore(reducer);
export default store;
import React, { Component } from 'React';
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
import { Input, Button, List } from 'antd';
// 引用store
import store from './store';
class TodlList extends Component {
constructor(props) {
super(props);
// 获取store,并赋值给state
this.state = store.getState();
}
render() {
return() {
return (
<div style={{marginTop: '10px', marginLeft: '10px'}}>
<div>
<Input value={this.state.inputValue} placeholder='todo info' style={{width: '300px'}} />
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
}
- 为了方便调试redux,可以先在chrome浏览器中安装插件 Redux DevTools
- 为了能正常使用,还要在给createStore方法添加一个参数。(按插件提示添加即可)
src/store/reducer.js中的内容
// 设置一个默认值,如果state没有值则使用这个默认值
const defaultState = {
inputValue:'123',
list: [1, 2]
}
// 导出一个函数(第一个参数是之前的state,第二个值数是一个action对象,包括类型和传值)
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === 'add_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if (action.type === 'delete_todo_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
src/store/index.js中的内容
// 引入createStore方法
import { createStore } from 'redux';
// 引入reducer
import reducer from './reducer';
// 创建store,并把reducer中导出的方法传进来
const store = createStore(reducer);
export default store;
在组件中的内容
import React, {Component} from 'react';
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
import { Input, Button, List } from 'antd';
// 引用store
import store from './store';
class TodoList extends Component {
constructor(props) {
super(props);
// 获取store,并赋值给state
this.state = store.getState();
// 统一在constructor中绑定this,提交性能
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleClick = this.handleClick.bind(this);
// 在组件中订阅store,只要store改变就触发这个函数
this.unsubscribe = store.subscribe(this.handleStoreChange);
}
// 当store状态改变时,更新state
handleStoreChange() {
// 用从store中获取的state,来设置state
this.setState(store.getState());
}
render() {
return (
<div style={{margin: '10px'}}>
<div className="input">
<Input
style={{width: '300px', marginRight: '10px'}}
value={this.state.inputValue}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleClick}>提交</Button>
</div>
<List
style={{marginTop: '10px', width: '300px'}}
bordered
dataSource={this.state.list}
renderItem={(item, index) => (<List.Item onClick={this.handleDelete.bind(this, index)}>{item}</List.Item>)}
/>
</div>
)
}
// 组件注销前把store的订阅取消
componentWillUnmount() {
this.unsubscribe();
}
// 输入内容时(input框内容改变时)
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
// 当input框中数据改变时,交给store去处理
store.dispatch(action);
}
// 添加一项
handleClick () {
const action = {
type: 'add_todo_item',
}
store.dispatch(action);
}
// 点击删除当前项
handleDelete (index) {
const action = {
type: 'delete_todo_item',
index
}
store.dispatch(action);
}
}
export default TodoList;
使用常量代替action中的type字符串
因为如果action的type中字符串,如果拼错了,不会报错,很难找到问题。但是使用常量如果写错了控制台会报错,方便排错。
const types = {
CHANGE_INPUT_VALUE: 'change_input_value',
ADD_TODO_ITEM: 'add_todo_item',
DELETE_TODO_ITEM: 'delete_todo_item'
}
export default types;
import types from './store/actionTypes';
...
...
// 输入内容时(input框内容改变时)
handleInputChange(e) {
const action = {
type: types.CHANGE_INPUT_VALUE,
value: e.target.value
}
// 当input框中数据改变时,交给store去处理
store.dispatch(action);
}
// 添加一项
handleClick () {
const action = {
type: types.ADD_TODO_ITEM,
}
store.dispatch(action);
}
// 点击删除当前项
handleDelete (index) {
const action = {
type: types.DELETE_TODO_ITEM,
value: index
}
store.dispatch(action);
}
import types from './actionTypes';
// 设置一个默认值,如果state没有值则使用这个默认值
const defaultState = {
inputValue: '',
list: ['学习英语', '学习React']
}
// 导出一个函数(第一个参数是之前的state,第二个值数是一个action对象,包括类型和传值)
export default (state = defaultState, action) => {
const { type, value } = action;
const newState = JSON.parse(JSON.stringify(state));
switch(type) {
case types.CHANGE_INPUT_VALUE:
newState.inputValue = value;
break;
case types.ADD_TODO_ITEM:
newState.list.push(newState.inputValue);
newState.inputValue = '';
break;
case types.DELETE_TODO_ITEM:
newState.list.splice(value, 1);
break;
default:
return state
}
return newState;
}
在store文件夹下创建一个actionCreators.js,把action都集中写在这个文件中统一管理。这样子看上去多了一个步骤,但是会方便后期维护和自动化测试。
具体代码如下
import types from './actionTypes';
// 返回的是action对象
export const inputChangeAction = (value) => ({
type: types.CHANGE_INPUT_VALUE,
value: value
})
export const addItemAction = (value) => ({
type: types.ADD_TODO_ITEM
})
export const deleteItemAction = (index) => ({
type: types.DELETE_TODO_ITEM,
value: index
})
import { inputChangeAction, addItemAction, deleteItemAction } from './store/actionCreators';
...
...
// 输入内容时(input框内容改变时)
handleInputChange(e) {
const action = inputChangeAction(e.target.value);
store.dispatch(action);
}
// 添加一项
handleClick () {
const action = addItemAction();
store.dispatch(action);
}
// 点击删除当前项
handleDelete (index) {
const action = deleteItemAction(index);
store.dispatch(action);
}
纯函数:纯函数指的是,给定固定的输入,就一定会返回固定的输出(所以不能有ajax请求、new Date()和定时器)。而且不会有任何副作用(如不能修改store的内容,要先复制一份再去操作)。
createStore:创建store仓库
store.dispatch:派发action,并传递给store
store.getState:获取store中所有的内容
store.subscribe:订阅store的改变