redux
- redux是一个专门用于做状态管理的JS库(不是react插件库)。
- 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
- 作用: 集中式管理react应用中多个组件共享的状态。
什么情况下需要使用redux
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
redux工作流程
redux的三个核心概念
action
动作的对象
包含2个属性
- type:标识属性, 值为字符串, 唯一, 必要属性
- data:数据属性, 值类型任意, 可选属性
例子:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }
reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的纯函数。
store
将state、action、reducer联系在一起的对象
如何得到此对象?
import {createStore} from ‘redux’
import reducer from ‘./reducers’
const store = createStore(reducer)
此对象的功能?
- getState(): 得到state
- dispatch(action): 分发action, 触发reducer调用, 产生新的state
- subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
redux的核心API
createstore()(以弃用可以用 legacy_createStore 代替)
作用:创建包含指定 reducer 的store对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
const initState = 0 export default function counterReducer(preState = initState, { type, data }) { console.log(preState,type, data); switch (type) { case 'increment': return preState + data*1 case 'decrement': return preState - data*1 default: return preState } }
|
1 2 3 4 5 6 7 8 9 10 11 12
|
import { legacy_createStore as createStore } from 'redux'
import countReducer from './count_reducer'
export default createStore(countReducer)
|
store对象
- 作用: redux库最核心的管理对象
- 它内部维护着:
- state
- reducer
- 核心方法:
- getState()
- dispatch(action)
- subscribe(listener)
- 具体编码:
- store.getState()
- store.dispatch({type:’INCREMENT’, number})
- store.subscribe(render)
1 2 3 4 5 6 7 8 9 10 11 12
|
import { legacy_createStore as createStore } from 'redux'
import countReducer from './count_reducer'
export default createStore(countReducer)
|
1 2 3 4 5 6
|
export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
import { INCREMENT, DECREMENT } from './constant'
const initState = 0 export default function counterReducer(preState = initState, { type, data }) { console.log(preState, type, data); switch (type) { case INCREMENT: return preState + data * 1 case DECREMENT: return preState - data * 1 default: return preState } }
|
1 2 3 4 5 6 7 8 9 10 11
|
import { INCREMENT, DECREMENT } from './constant'
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| import React, { Component } from 'react'
import store from '../../redux/store' import {createIncrementAction,createDecrementAction} from '../../redux/count_actions'
export default class Count extends Component {
increment = () => { const { value } = this.selectNumber store.dispatch(createIncrementAction(value)) }
decrement = () => { const { value } = this.selectNumber store.dispatch(createDecrementAction(value)) }
incrementIfOdd = () => { const { value } = this.selectNumber
if (store.getState() % 2 !== 0) { store.dispatch(createIncrementAction(value)) } }
incrementAsync = () => { const { value } = this.selectNumber
setTimeout(() => { store.dispatch(createIncrementAction(value)) }, 1000) }
render() { return ( <div> <h1>当前求和为:{store.getState()}</h1> <select ref={(c) => (this.selectNumber = c)} name="" id=""> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<App />)
store.subscribe(() => { root.render(<App />) })
|
applyMiddleware()
作用:应用上基于redux的中间件(插件库)
combineReducers()
作用:合并多个reducer函数
redux异步编程
理解:
- redux默认是不能进行异步处理的,
- 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
使用异步中间件
npm install –save redux-thunk
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
import { INCREMENT, DECREMENT } from './constant'
export const createIncrementAction = data => ({ type: INCREMENT, data })
export const createDecrementAction = data => ({ type: DECREMENT, data })
export const createIncrementAsyncAction = (data, time) => { return (dispatch) => { setTimeout(() => { dispatch(createIncrementAction(data)) }, time) } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
import countReducer from './count_reducer'
import thunk from 'redux-thunk'
export default createStore(countReducer, applyMiddleware(thunk))
|
react-redux
理解
- 一个react插件库
- 专门用来简化react应用中使用redux
react-Redux将所有组件分成两大类
- UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
- 容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
相关API
- Provider:让所有组件都可以得到state数据
- connect:用于包装 UI 组件生成容器组件
- mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
- mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
react-redux数据共享
1 2 3 4 5 6 7 8 9 10 11 12 13
| import React from "react"; import ReactDOM from "react-dom/client"; import App from "./App"; import store from './redux/store' import { Provider } from 'react-redux'
const root = ReactDOM.createRoot(document.getElementById('root')) root.render( <Provider store={store}> <App /> </Provider> )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import React, { Component } from 'react' import Count from './containers/Count' import Person from './containers/Person' export default class App extends Component { render() { return ( <div> <Count /> <hr /> <Person/> </div> ) } }
|
1 2 3 4 5 6 7 8
|
export const INCREMENT = 'increment' export const DECREMENT = 'decrement'
export const ADD_PERSON = 'add_person'
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
import { INCREMENT, DECREMENT } from '../constant'
export const increment = data => ({ type: INCREMENT, data })
export const decrement = data => ({ type: DECREMENT, data })
export const incrementAsync = (data, time) => { return (dispatch) => { setTimeout(() => { dispatch(increment(data)) }, time) } }
|
1 2 3 4 5
| import { ADD_PERSON } from '../constant'
export const addPerson = personObj => ({ type: ADD_PERSON, data: personObj })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
import { INCREMENT, DECREMENT } from '../constant'
const initState = 0 export default function counterReducer(preState = initState, { type, data }) { switch (type) { case INCREMENT: return preState + data * 1 case DECREMENT: return preState - data * 1 default: return preState } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { ADD_PERSON } from '../constant'
const initState = [{ id: '001', name: 'tom', age: 18 }] export default function personReducer(preState = initState, { type, data }) {
switch (type) { case ADD_PERSON: return [data, ...preState]
default: return preState } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
import { combineReducers } from 'redux'
import count from './count'
import persons from './person'
export default combineReducers({ count, persons })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
import { legacy_createStore as createStore, applyMiddleware } from 'redux'
import allReducer from './reducer'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| import React, { Component } from 'react'
import { increment, decrement, incrementAsync } from '../../redux/actions/count'
import { connect } from 'react-redux'
class Count extends Component { increment = () => { const { value } = this.selectNumber this.props.increment(value) }
decrement = () => { const { value } = this.selectNumber this.props.decrement(value) }
incrementIfOdd = () => { const { value } = this.selectNumber if (this.props.count % 2 !== 0) { this.props.increment(value) } }
incrementAsync = () => { const { value } = this.selectNumber this.props.incrementAsync(value, 1000) }
render() { const { count,persons } = this.props return ( <div> <h2>我是Count组件,下方组件人数为{persons.length}</h2> <h4>当前求和为:{count}</h4> <select ref={(c) => (this.selectNumber = c)} name="" id=""> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> </select> <button onClick={this.increment}>+</button> <button onClick={this.decrement}>-</button> <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> <button onClick={this.incrementAsync}>异步加</button> </div> ) } }
export default connect( (state) => ({ count: state.count,persons:state.persons }),
{ increment, decrement, incrementAsync, } )(Count)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import React, { Component } from 'react' import { connect } from 'react-redux' import { nanoid } from 'nanoid' import { addPerson } from '../../redux/actions/person'
class Person extends Component { addPerson = () => { const name = this.nameNode.value const age = this.ageNode.value * 1 const personObj = { id: nanoid(), name, age, } this.nameNode.value = '' this.ageNode.value = '' this.props.addPerson(personObj) }
render() { const { persons, count } = this.props return ( <div> <h2>我是Person组件,上方组件和为{count}</h2> <input ref={(c) => (this.nameNode = c)} type="text" placeholder="请输入名字" /> <br /> <input ref={(c) => (this.ageNode = c)} type="text" placeholder="请输入年龄" /> <br /> <button onClick={this.addPerson}>添加</button> <br /> <ul> {persons.map((v) => { return ( <li key={v.id}> 名字{v.name}---年龄{v.age} </li> ) })} </ul> </div> ) } }
export default connect((state) => ({ persons: state.persons, count: state.count }), { addPerson, })(Person)
|
使用上redux调试工具
安装chrome浏览器插件

下载工具依赖包
1
| npm install --save-dev redux-devtools-extension
|
纯函数和高阶函数
纯函数
一类特别的函数: 只要是同样的输入(实参),必定得到同样的输出(返回)
必须遵守以下一些约束
不得改写参数数据
不会产生任何副作用,例如网络请求,输入和输出设备
不能调用Date.now()或者Math.random()等不纯的方法
redux的reducer函数必须是一个纯函数
高阶函数
- 理解: 一类特别的函数
- 情况1: 参数是函数
- 情况2: 返回是函数
- 常见的高阶函数:
- 定时器设置函数
- 数组的forEach()/map()/filter()/reduce()/find()/bind()
- promise
- react-redux中的connect函数
- 作用: 能实现更加动态, 更加可扩展的功能