Redux 学习笔记
什么是Redux
Redux 是 JavaScript 状态容器, 提供可预测化的状态管理。它专门为React.js这门框架设计,但并不是只能用于react,可以用于任何界面库。
Redux的设计原则
Redux在设计与实现时,遵循三大原则或者说规范限制
单一数据源
整个应用程序的转态数据仅存储子一个Store中,数据
只读的 State
唯一的 Store 也是只读的,应用程序无法直接修改状态数据。Store 对象本身的 API 非常少,仅有四个方法:
getState():获取当前状态数据dispatch(action):推送触发变化subscribe(listener):订阅数据变化replaceReducer(nextReducer):
显而易见,没有提供设置状态的方法。其中的 dispatch(action) 是唯一改变数据的方法,比如:
var action = {
type: 'ADD_USER',
user: {name: 'chuonye.com'}
};
store.dispatch(action);
dispatch 方法会将 action 传递给 Redux,action 就是一个普通对象,包含触发的操作类型以及数据。
使用纯函数执行状态的更新
Redux 接收到 action 后,会使用一个纯函数来处理,这些函数被称为 Reducers:
var someReducer = function(state, action) {
...
return state;
}
Reducer 接收当前的 state 和 action 作为参数,它也不能直接修改原有的数据,而是通过返回一个新的 state 来修改。总的来说,Redux 是一个帮助应用统一管理状态数据的工具,它遵循严格的单向数据流(Store 只读)设计,使得应用的行为变得可预测且容易理解。
使用Redux的基本流程
在介绍使用Redux的基本使用流程前,我们基于原生的方法实现TodoList的案例,在此基础之上分别演示Redux和React-Redux的使用流程
原生React的实现TodoList
示例:
import React, { Component } from 'react';
import {Row,Col,Button,Divider,Input,List,} from 'antd';
class TodoList extends Component {
constructor(props) {
super(props)
this.state = {
todos: ["篮球", "足球", "羽毛球"],
inpVal: ""
}
}
handleChange = (e) => {
this.setState({ inpVal: e.target.value })
}
handleClick = () => {
const { inpVal } = this.state
let todos = this.state.todos
todos.push(inpVal)
this.setState({ todos })
}
handleDelete = (id) => {
let {todos} = this.state
todos = todos.filter((v,index) => index != id)
this.setState({todos})
}
render() {
return (
<div>
<h3>React原生实现TodoList</h3>
<Divider />
<Input
onChange={this.handleChange}
placeholder="请输入"
></Input>
<Button onClick={this.handleClick}>添加</Button>
<Divider />
<Row>
<Col span={6}>
<List
bordered
dataSource={this.state.todos}
renderItem={(item, index) => (
<List.Item
extra={<a onClick={this.handleDelete.bind(this,index)}>删除</a>}
>{item}</List.Item>)}
>
</List>
</Col>
</Row>
</div>
)
}
}
export default TodoList
基于Redux实现TodoList
安装 Redux
yarn add redux --save
基于Redux实现TodoList的具体工作步骤:
- 创建 Action
- 根目录下创建
action文件夹并在文件夹下创建index.jsx - 构建
action函数用来返回action对象,注意action必须包含type属性 - 将
action并导出
- 根目录下创建
示例:
// src/action/index.jsx
// 创建Todo的Action
const addTodoList = (iptVal) => ({ type: 'ADD', iptVal })
// 查询当前的TodoList
const seleteTodoList = () => ({ type: 'SELECT' })
// 监听Input
const InputChange = (iptVal) => ({ type: 'INPUT', iptVal })
// 删除Todo的Action
const DeleteTodoList = (index) => ({ type: 'DELETE', index })
module.exports = {
addTodoList,seleteTodoList,InputChange,DeleteTodoList
}
- 创建 Reducer
- 根目录下创建
reducer文件夹并在文件夹下创建index.jsx - 构建
reducer,注意reducer要接收两个参数 - 第一个参数是默认状态,定义一个初始化的
state,然后进行赋值
- 根目录下创建
示例:
/src/reducer/index.jsx
// 1. 初始化
const initState = {
todos: ["篮球", "足球", "羽毛球"],
iptVal: ""
}
// 2. 创建reducer
const reducer = (state = initState, action) => {
let newState = Object.assign({}, state, action);
switch (action.type) {
case "ADD":
const { iptVal, todos } = state
newState.todos.push(iptVal)
return newState
case 'SELECT':
return Object.assign({}, state, action)
case 'INPUT':
return Object.assign({}, state, action)
case 'DELETE':
const { index } = action
newState.todos.splice(index, 1)
return newState
default:
return state;
}
}
export default reducer
- 创建 Store
- 跟目录下创建
store的文件夹并且在文件夹下创建index.jsx - 使用
createStore函数里面第一个参数接收的是reducer - 导入
reducer设置到函数中createStore(reducer) - 将创建的
store进行导出
- 跟目录下创建
示例:
import { createStore } from 'redux'
import reducer from './../reducer/'
const store = createStore(reducer)
export default store
- 组件中使用Redux
- 绑定按钮监听事件
- 在组件加载完毕的时候通过
store进行监听器的注册,返回值可以用来监听注册 - 通过
store.dispatch(action)发送action 示例:
import React, { Component } from 'react';
import {
Divider,
Input,
Button,
Row,
Col,
List
} from 'antd'
import store from '../../store/index'
import {
addTodoList,
seleteTodoList,
InputChange,
DeleteTodoList
} from './../../action/index'
class Home extends Component {
constructor(props) {
super(props)
this.state = store.getState()
}
componentWillMount() {
const action = seleteTodoList()
// 发送action
store.dispatch(action)
}
handleChange = (e) => {
const action = InputChange(e.target.value)
// 发送action
store.dispatch(action)
}
handleClick = () => {
const { iptVal } = this.state
const action = addTodoList(iptVal)
// 发送action
store.dispatch(action)
}
handleDelete = (index) => {
const action = DeleteTodoList(index)
// 发送action
store.dispatch(action)
}
componentDidMount() {
store.subscribe(() => this.setState(store.getState()))
}
render() {
return (
<div
style={{
width: 500, height: 500, textAlign: 'center',
margin: '0 auto'
}}
>
<h3>Redux实现TodoList</h3>
<Divider />
<Input
onChange={this.handleChange}
placeholder="请输入"
style={{ width: 300, marginRight: 50 }}
value={this.state.iptVal}
></Input>
<Button onClick={this.handleClick}>添加</Button>
<Divider />
<Row>
<Col>
<List
bordered
dataSource={this.state.todos}
renderItem={(item, index) => (
<List.Item
extra={<span onClick={this.handleDelete.bind(this, index)}>删除</span>}
>{item}</List.Item>)}
>
</List>
</Col>
</Row>
</div>
)
}
}
export default Home;
Redux调用流程的总结
基本的使用步骤:
- 创建
action - 发送
action至reducer--store.dispatch(action) reducer(preState,action)进行业务逻辑的处理返回给store- 监听
store的变化,基于store提供的apistore.getState()获取最新的state
action --> store.dispatch(action) --> reducer(action) --> store
基于React-Redux实现TodoList
创建项目
create-react-app demo02
cd demo02
yarn add redux --save
yarn add react-redux --save
yarn add antd@"^3.26.20" --save
yarn add react@"^16.14.0" --save
实现步骤
- 基于
redux构建store,在src下创建store目录并创建index.jsx文件 - 创建
reducer/index.jsx文件,构建reducer响应action - 通过
createStore把reducer注入返回store - 在
src/App.js引入store并导入Provider将整个结构进行包裹,并且传递store - 引入
connect对组件进行加强,mapDispatchToProps返回的是一个对象{key:方法名,value:调用dispatch发送action},在组件中通过this.props可以获取到该对象 。connect 函数的基本介绍: | 参数名 | 类型 | 说明 | | -------------------------------------------- | -------- | ------------------------------------------------------------ | | mapStateToProps(state,ownProps) | Function | 该函数将 store 中的数据作为 props 绑定到到组件
state:redux 中的 store
ownProps:组件中的 props | | mapDispatchToProps(dispatch,ownProps) | Function | 将 action 作为 props 绑定到组件中
dispatch:就是 store.dispatch()
ownProps:组件中的props | | mergeProps(stateProps,dispachProps,ownProps) | Function | 不管是stateProps,dispachProps 都需要和ownProps 合并以后赋给组件,通常情况下可以不传递这个参数connect会使用Object.assign替代该方法 | | options | Object | 定制 connector 的行为 |
构建 reducer
// src/reducer/index.jsx
const initState = {
todos: ["篮球", "足球", "羽毛球"],
}
const rootReducer = (state = initState, action) => {
if (action.type === "ADD") {
const { iptVal } = action
let newState = JSON.parse(JSON.stringify(state))
newState.todos.push(iptVal)
return newState
} else if (action.type === 'SELECT') {
let newState = JSON.parse(JSON.stringify(state))
return newState
} else if (action.type === "DELETE") {
const { index } = action
let newState = JSON.parse(JSON.stringify(state))
newState.todos.splice(index, 1)
return newState
} else {
return state
}
}
export default rootReducer
构建 store
// src/store/index.jsx
import { createStore } from "redux";
import reducer from "../reducer";
const store = createStore(reducer)
export default store
导入Provider传递store
// App.jsx
import React, {
Component
} from 'react';
import {
Provider
} from 'react-redux'
import store from './store';
import AddTodo from './pages/AddTodo';
import TodoList from './pages/TodoList'
import './App.css';
class App extends Component {
render() {
return (
<div style={{ width: 500, height: 500, textAlign: 'center', margin: '0 auto' }}>
<Provider store={store}>
<h3>React-Reudx实现TodoList</h3>
<AddTodo />
<br />
<TodoList />
</Provider>
</div>
);
}
}
export default App;
AddTodo 组件
import React, { Component } from 'react';
import {
Input,
Button,
} from 'antd'
import { connect } from 'react-redux'
class AddTodo extends Component {
constructor(props) {
super(props);
this.state = {
iptVal: ""
}
}
handleClick = () => {
const { iptVal } = this.state
this.props.AddAction(iptVal)
}
render() {
return (
<div>
<Input
onChange={(e) => this.setState({ iptVal: e.target.value })}
placeholder="请输入"
style={{ width: 300, marginRight: 50 }}
value={this.state.iptVal}
></Input>
<Button onClick={this.handleClick}>添加</Button>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
AddAction: (iptVal) => {dispatch({type: 'ADD',iptVal})}
}
}
export default connect(null, mapDispatchToProps)(AddTodo);
TodoList 组件
import React, { Component } from 'react';
import {
Row,
Col,
List
} from 'antd'
import { connect } from 'react-redux'
class TodoList extends Component {
handleDelete = (index) => {
this.props.RemoveAction(index)
}
render() {
return (
<div>
<Row>
<Col>
<List
bordered
dataSource={this.props.todos}
renderItem={(item, index) => (
<List.Item
extra={<span onClick={this.handleDelete.bind(this, index)}>删除</span>}
>{item}</List.Item>)}
>
</List>
</Col>
</Row>
</div>
);
}
}
const mapStateToProps = (state) => {
return state
}
const mapDispatchToProps = (dispatch) => {
return {
RemoveAction: (index) => {dispatch({type: 'DELETE',index})}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
本文参考: