目录
1.概述
Redux:数据层框架。
Redux = Reducer + Flux
核心:将所有数据放到公共store中,蓝的组件改变store中数据,灰的组件自动感知数据变化,获取数据,进而发生改变,实现间接的组件间数据传递功能。
2.Redux工作流程
3.使用antd编写TodoList页面布局
antd:https://ant.design/docs/react/introduce-cn
使用前先安装:npm install antd --save(使用插件之前一定先进行安装)
4.Redux实现TodoList
首先安装redux:npm install --save redux
(1)创建store
(2)Action 和 Reducer 的编写
(3)React + Redux实现TodoList实例
(此例中尚不完整,尚不完全合理,有待进行ActionTypes的拆分 和 actionCreator的统一创建)
1.目录结构
2.index.js(入根组件,入口)
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
3.TodoList.js(组件)
import React, {Component} from 'react';
import 'antd/dist/antd.css';
import {Input, Button, List} from 'antd';
import store from './store/index';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
store.subscribe(this.handleStoreChange) //store中数据改变后执行的方法
}
render() {
return (
<div style={{marginTop: 10, marginLeft: 10}}>
<div>
<Input value={this.state.inputValue}
placeholder="todo info"
style={{width: 300, marginRight: '10px'}}
onChange={this.handleInputChange}/>
<Button type="primary" onClick={this.handleButtonClick}>提交</Button>
<List style={{marginTop: 10, width: 300}}
bordered
dataSource={this.state.list}
renderItem={(item,index) => (
<List.Item onClick={this.handleItemDelete.bind(this,index)}>{item}</List.Item>
)}
/>
</div>
</div>
)
}
handleInputChange(e) {
const action = {
type: 'change_input_value',
value: e.target.value
};
store.dispatch(action);
}
handleStoreChange(){
this.setState(store.getState()); //执行setState后,页面才会重新渲染
}
handleButtonClick(){
const action={
type: 'add_todo_item'
};
store.dispatch(action)
}
handleItemDelete(index){
const action={
type: 'del_todo_item',
index: index
};
store.dispatch(action)
}
}
export default TodoList;
4.store/index.js(创建store)
import {createStore} from "redux";
import reducer from './reducer';
const store = createStore(reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
5.store/reducer.js(Reducer)
const defaultState = {
inputValue: '',
list: []
};
//reducer 可以接受state,但绝不能修改state,所以进行拷贝后修改
export default (state=defaultState, action)=>{ //state数据、action都是从store中来的
if(action.type==='change_input_value'){
const newState = JSON.parse(JSON.stringify(state));//深拷贝state??
newState.inputValue = action.value;
return newState; //将改变的state返回给store
}
else if(action.type==='add_todo_item'){
const newState = JSON.parse(JSON.stringify(state));//深拷贝state??
newState.list.push(newState.inputValue);
// newState.list = [...newState.list, newState.inputValue]; //这种方法也可以
newState.inputValue = '';
return newState;
}
else if(action.type==='del_todo_item'){
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index,1);
return newState;
}
return state;
}
5.ActionTypes的拆分
由于在编写代码时,action中的type字符串容易写错,导致程序不能运行;可以在actionTypes.js中定义type字符串的常量,在定义action的type时,可以使用常量,这样不容易出错。
//TodoList.js
import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DEL_TODO_ITEM} from './store/actionTypes';
handleInputChange(e) {
const action = {
type: CHANGE_INPUT_VALUE,
value: e.target.value
};
store.dispatch(action);
}
//actionTypes.js(新增的用于定义常量的)
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DEL_TODO_ITEM = 'del_todo_item';
//reducer.js
import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DEL_TODO_ITEM} from './actionTypes';
if(action.type === CHANGE_INPUT_VALUE){
const newState = JSON.parse(JSON.stringify(state));//深拷贝state??
newState.inputValue = action.value;
return newState; //将改变的state返回给store
}
6.使用actionCreator统一创建action
在上述事例中(4.Redux实现TodoList),在TodoList.js 中需要多次创建action,应该使用actionCreator统一创建action。完整实例如下:
(1)目录结构
(2)index.js(同上)
(3)TodoList.js
import React, {Component} from 'react';
import 'antd/dist/antd.css';
import {Input, Button, List} from 'antd';
import store from './store/index';
import {getInputChangeAction, getAddItemAction, getDeleteItemAction} from './store/actionCreators';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleButtonClick = this.handleButtonClick.bind(this);
store.subscribe(this.handleStoreChange) //store中数据改变后执行的方法
}
render() {
return (
<div style={{marginTop: 10, marginLeft: 10}}>
<div>
<Input value={this.state.inputValue}
placeholder="todo info"
style={{width: 300, marginRight: '10px'}}
onChange={this.handleInputChange}/>
<Button type="primary" onClick={this.handleButtonClick}>提交</Button>
<List style={{marginTop: 10, width: 300}}
bordered
dataSource={this.state.list}
renderItem={(item,index) => (
<List.Item onClick={this.handleItemDelete.bind(this,index)}>{item}</List.Item>
)}
/>
</div>
</div>
)
}
handleInputChange(e) {
const action=getInputChangeAction(e.target.value);
store.dispatch(action);
}
handleStoreChange(){
this.setState(store.getState()); //执行setState后,页面才会重新渲染
}
handleButtonClick(){
const action = getAddItemAction();
store.dispatch(action)
}
handleItemDelete(index){
const action = getDeleteItemAction(index)
store.dispatch(action)
}
}
export default TodoList;
(4)store/index.js(同上)
(5)store/reducer.js
import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DEL_TODO_ITEM} from './actionTypes'
const defaultState = {
inputValue: '',
list: []
};
//reducer 可以接受state,但绝不能修改state,所以进行拷贝后修改
export default (state=defaultState, action)=>{ //state数据、action都是从store中来的
if(action.type === CHANGE_INPUT_VALUE){
const newState = JSON.parse(JSON.stringify(state));//深拷贝state??
newState.inputValue = action.value;
return newState; //将改变的state返回给store
}
else if(action.type === ADD_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));//深拷贝state??
newState.list.push(newState.inputValue);
// newState.list = [...newState.list, newState.inputValue]; //这种方法也可以
newState.inputValue = '';
return newState;
}
else if(action.type === DEL_TODO_ITEM){
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index,1);
return newState;
}
return state;
}
(6)store/actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DEL_TODO_ITEM = 'del_todo_item';
(7)store/actionCreator.js
import {CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DEL_TODO_ITEM} from './actionTypes';
export const getInputChangeAction = (value)=>({
type: CHANGE_INPUT_VALUE,
value
});
export const getAddItemAction = ()=>({
type: ADD_TODO_ITEM,
});
export const getDeleteItemAction = (index)=>({
type: DEL_TODO_ITEM,
index
});
7.Redux设计和使用的三项原则:
(1)store是唯一的;
(2)只有store能够改变自己的内容;(当Reducer处理完数据后,将其返回给store,store根据返回的数据自己更新自己的数据;而state就是store中的数据,因此传给Reducer的是state,并且处理数据时要拷贝state,以免改变store中的state数据。)
(3)Reducer必须是纯函数。(纯函数:给定固定的输入,就一定会有固定的输出,而且不会有任何副作用。)