Redux基础概念
在了解Redux之前首先思考一个问题:为什么要使用Redux?
React是一个轻量级的视图层框架,组件之间的通信方式是这样的:【学习笔记 】React ② 组件拆分以及组件间通信
- 父组件通过属性的形式向子组件传递数据,既可以传递数据又可以传递方法;子组件通过this.props中接收传递过来的方法和数据
- 子组件调用父组件的方法,修改父组件的内容子组件通过
this.props.func()
就可以调用父组件的方法,父组件传递的函数this
指向要做绑定,借助这个方法对父组件的数据进行修改
当我们在实现一些简单项目的时候,是没有任何问题的。但是如果开发发型复杂项目,组件之间的通信就会变得复杂,产生许多不利维护的代码。上面的图可以形象的表现出引入Redux的优势,把数据放在store进行管理,一个组件改变了store里面的数据,其他组件就会感知到这个变化,从而进行渲染。
Redux工作流
将Redux当作一个图书管理系统,react components
看成一个借书的用户,action creators
看作‘要借什么书’这句话,store
看作图书管理员,reducer
看作是图书管理员的笔记本。下图很形象表示出了这一流程。
components
发出action
,通过store.dispatch()
告知store
。store
则通过与reducer
通信,获取到newState
。将新的state
传递给components
,完成通信。这就是redux的工作流。依照这个工作流,对Todolist工程进行改造。
一、使用Ant Design重新编写Todolist页面布局
- 安装Ant Design
yarn add antd
- 在
Todolist.js
中引入样式文件以及需要的组件
import React, {Fragment, Component} from 'react';
import TodoItem from './TodoItem'
import 'antd/dist/antd.css'; // or 'antd/dist/antd.less'
import { Input, Button, List } from 'antd';
import store from './store'
class Todolist extends React.Component{
constructor (props) {
super(props)
}
render() {
// console.log('render')
return (
<Fragment>
<div>
<Input value={this.state.inputValue}
placeholder={'to do info'}
style={{width: '300px', marginRight: '10px'}}
/>
<Button type="primary">提交</Button>
</div>
<List
style={{width: '300px', marginTop: '10px'}}
bordered
dataSource={this.state.list}
renderItem={item => (
<List.Item>{item}</List.Item>
)}
/>
</Fragment>
);
}
}
export default Todolist;
在浏览器效果如下:
二、实现Redux Flow
- 安装 redux
yarn add redux
- createStore
在src
目录下新建store
文件夹,在该文件夹下新建index.js
文件
// 创建store
import { createStore } from 'redux'
const store = createStore();
export default store
- 创建reducer
在store
文件夹下新建reducer.js
文件
export default (state , action) => {
return state
}
- 将reducer传递给createStore
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
// const store = createStore(reducer);
// 增加 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() 是为了使用Reduce DevTools
export default store
- action 和 reducer 的编写
以通过实现Todolist基本功能为例来实现action
和reducer
的编写。基本功能如下:
(1)在input
文本框中输入待办事项
(2)点击提交按钮,将待办事情渲染在页面上,清空文本框内容
(3)点击某项待办事项,可将其删除
1.在store
中设置基础数据
const defaultState = {
inputValue: '',
list: []
}
export default (state = defaultState, action) => {
console.log(state, action)
return state
}
2.在Todolist.js
中通过store.getState()
获取store
中的数据,将inputValue
绑定在input
文本框,并在文本框绑定onChange()
事件
<Input value={this.state.inputValue} placeholder={'to do info'} style={{width: '300px', marginRight: '10px'}} onChange={this.handleInputChange} />
handleInputChange(e) {
const action = {
type: 'change_input_value', // 描述这个action是干嘛的
value: e.target.value
}
store.dispatch(action)
}
3.完成store
和reducer
之间的通信
// reducer 可以接收state,但是绝不能修改state
export default (state = defaultState, action) => {
if (action.type === 'change_input_value') {
// 深拷贝 state
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value
return newState
}
console.log(state, action)
return state
}
以上就实现了第一个基本功能,store中的值是否随着input文本框的输入而改变?我们在浏览器中看一下效果,
我们可以看到state
中的值确实随着input
文本框中输入的内容发生改变而改变。后面两个功能也类似,只需要为Button
和List.Item
绑定对应的事件,按照上面流程修改reducer
即可。
handleBtnClick () {
const action = {
type: 'add_todo_item'
}
store.dispatch(action)
}
handleItemDelete (index) {
const action = {
type: 'del_todo_list_item',
value: index
}
store.dispatch(action)
}
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 === 'del_todo_list_item') {
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.value, 1)
return newState
}
三、actionCreator的统一创建
以上的代码虽可以实现ReduxFlow,但是从可维护性,以及前端自动化测试方面体验都不是很好,因此我们需要将actionCreator
做统一管理。
在store
文件夹下新建actionTypes.js
和actionCreators.js
,分别管理type
和action
// actionTypes.js
export const CHANGE_INPUT_VALUE = 'change_input_value'
//actionCreators.js
import { CHANGE_INPUT_VALUE } from './actionTypes'
export const getInputChangeAction = (value) => ({
type: CHANGE_INPUT_VALUE,
value
})
// Todolist.js
handleInputChange(e) {
const action = getInputChangeAction(e.target.value)
store.dispatch(action)
}
一些零散的点
redux设计和使用的三项原则
1.store是唯一的
2.只有store能改变自己的内容
3.reducer必须是纯函数核心API
1.createStore
2.store.dispatch
3.store.getState
4.store.subscribe
(完)