Redux原理及工作流程和使用方法(四)
一、Redux结构图
Redux的三个非常重要的组成部分:
- action
- reducer
- store
action 通知 reducer 修改 state,store 管理 state。
各部分的身份
把这个过程比拟成图书馆的一个流程来帮助理解。
- React Component(借书的人 )
需要借书的人 - Action Creator(具体借书的表达)
想借书的人向图书馆管理员说明要借的书的那句话。 - Store(图书馆管理员)
负责整个图书馆的管理。是Redux的核心 - Reducers(图书馆管理员的记录本)
管理员需要借助Reducer(图书馆管理员的记录本)来记录。
工作流程
借书的人(ReactComponent)说了一句话(Action Creator)向图书馆管理员(Store)借一本书,图书馆管理员年纪大了啊记不住啊,便掏出了自己的记录本(Reducers)。看了看知道了那本书有没有,在哪,怎么样。这样一来管理员就拿到了这本书,再把这本书交给了借书人。
翻译过来就是:
组件想要获取State, 用ActionCreator创建了一个请求交给Store,Store借助Reducer确认了该State的状态,Reducer返回给Store一个结果,Store再把这个State转给组件。
二、实例代码:
//入口文件:index.js
ReactDOM.render(
<TodoList/>,
document.getElementById('root')
)
//TodoList组件
import React, { Component } from "react";
import { Input, Button, List } from "antd";//通过antd栈官网引入需要的组件
import 'antd/dist/antd.css';//引入antd栈的样式文件
//列表中要显示的数据
const data = [
'Racing car sprays burning fuel into crowd.',
'Japanese princess to wed commoner.',
'Australian walks 100km after outback crash.',
'Man charged over missing wedding girl.',
'Los Angeles battles huge wildfires.',
];
//TodoList
class TodoList extends Component{
render() {
return(
<div style={{marginTop:'10px', marginLeft:'10px'}}>
<div >
<Input placeholder = 'qq' style = {{width:'300px', marginRight:'10px'}} />{/* 属性placeholder:默认显示的值*/}
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop:'10px', width:'300px'}}
bordered
dataSource={data}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
export default TodoList;
三、使用Redux
1、安装Redux
2、创建Store
在src下新建一个文件夹store,然后创建一个index.js文件,用户存放store中的代码:
//首先从redux这个第三方模块引入createStore方法,调用这个方法就可以创建一个store
import { createStore } from 'redux';
const store = createStore();//创建store
export default store;//导出store
3、引出reducer这个身份
仍然通过图书馆的例子进行分析:store
这个管理员记不住管理的那么多数据,需要一个小的记录本帮助他(store)
管理数据,所以在创建store
的时候需要将这个记录本传递给他(store)
,这样这个store
才可以知道自己管理的数据
接下来创建这个记录本(Reducer)
在新建的文件夹store下创建reducer.js文件
//reducer存放数据
//reducer的内容需要返回一个函数,这个函数接受两个参数state,action,
//state是整个store存储的数据
const defaultState = {//初始化数据
inputValue:'213',
list:[1,32]
};
export default (state = defaultState, action) => {
return state;
}
4、将reducer传递给store
./store/index.js
//store的内容
import { createStore } from 'redux';
//引入reducer
import reducer from './reducer'
const store = createStore(reducer);//创建store,并将reducer传递给store
export default store;//导出store
到此store就创建好了,接下来在组件中可以从store中取数据了
5、组件从store中取数据
./src/TodoList .js组件
import React, { Component } from "react";
import { Input, Button, List } from "antd";//通过antd栈官网引入需要的组件
import 'antd/dist/antd.css';//引入antd栈的样式文件
import store from './store'//引入store
class TodoList extends Component{
constructor(props){
super(props);
this.state = store.getState();//获取store中的数据
}
render() {
return(
<div style={{marginTop:'10px', marginLeft:'10px'}}>
<div >
<Input value={this.state.inputValue} placeholder = 'qq' style = {{width:'300px', marginRight:'10px'}} />{/* 属性placeholder:默认显示的值*/}
<Button type="primary">提交</Button>
</div>
<List
style={{marginTop:'10px', width:'300px'}}
bordered
dataSource={this.state.list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
export default TodoList;
以上完成了从store中取数据的功能
四、实现改变store中的数据:
在实现改变store中的数据之前先安装个redux调试工具(插件Redux DevTools),通过该插件可以很方便的对redux进行调试
问题:
解决:在创建store的方法中增加个参数window.REDUX_DEVTOOLS_EXTENSION && window.REDUX_DEVTOOLS_EXTENSION()方可使用调试工具:
./store/index.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
仍以图书馆的例子说明:
假如一个人要去图书馆借书,他要对管理员store说“我要借书”这句话,但是这句话是通过Action(Creators)创建的,首先创建这句话(创建action):
Action是一个对象的形式
- Type:告诉store要做的事情是什么?
- Value:传过去一个结果
创建好action后,要把这句话传给store,那如何传给store呢?
Store提供了一个方法(dispatch),调用这个方法就可以将action传给store了
当input输入框发生改变的时候,store中对应的值要发生改变!
import React, { Component } from 'react';
import { Input, Button, List } from 'antd';
import store from './store/index';
import 'antd/dist/antd.css';
class TodoList extends Component{
constructor(props){
super(props);
this.state = store.getState();
this.handleValueChange = this.handleValueChange.bind(this)
}
handleValueChange(e){
const action = {
type:'change_inpue_value',
value:e.target.value
}
store.dispatch(action);
}
render(){
return(
<div style={{marginTop:'10px', marginLeft:'10px'}}>
<div>
<Input value={this.state.inputValue} placeholder='ww'
style = {{width: '300px', marginRight:'10px'}}
onChange = {this.handleValueChange}/>
<Button type="primary">提交</Button >
</div>
<List
style = {{width: '300px', marginTop:'10px'}}
bordered
dataSource={this.state.list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
export default TodoList;
现在已经把action传递给了store。但是这个管理员store不知道应该怎么做,需要去查记录表(reducer),
那么如何去查记录本(reducer)呢?他(stroe)要把当前stroe里的数据和action一起传给记录本(reducer)
很好的一件事情,当store接受到action后,Store会自动的把当前store中存储的数据(旧数据)和接受的action一起传给reducer,有reducer去告诉store要去做什么。
现在reducer就接受到了之前的数据,也拿到了action这句话,现在就通过reducer来做这件事情,最后将newState返回给了store
Store变化了,组件更新:需要在组件上做个处理
组件订阅store,这样的话,store一旦发生变化,subscribe中的函数就会执行
当store中的数据发生了变化,就会执行函数,从store中重新取一会数据,来跟新state
actionTypes的拆分
在以上代码中,action的type定义为一个字符串,当这个出现错误时,控制台不会有错误提示,对排错带来给很大的不变,
这样我们把这个type抽取出来,放到一个单独的文件中,定义为常量,当常量在引用的时候,出项错误,控制台就会有错误提示
。即可解决这个问题
定义actionType的常量
在TodoList的组件中引入定义的常量
使用actionCreator统一创建action
1、创建actionCreators.js
2、组件中使用