目录
- redux
- 定义
- 使用时机
- redux基本概念
- Store
- State
- Action
- reducer
- redux工作原理
- 语法
- [1] 创建Store
- createStore
- combineReducers
- [2]创建并分发action
- dispatch
- applyMiddleware
- 语法
- 举例说明- 做一个加法运算
- 执行原理
- redux-thunk中间件使用
- [3]创建reducer
- 语法
- 渲染过程
- [4]getState
- [5]subscribe
- 举例说明
- 配置redux
- [1] 下载
- [2] 配置
- react-redux
- 语法
- connect
- Provider组件
- 监听
- 配置
- [1]下载
- [2]配置
redux
redux中文文档
定义
Redux 是 JavaScript 状态容器,提供可预测化的状态管理 --> 是一个专门做状态管理的js库。
redux并不是react的插件库 --> Redux 除了和 React 一起用外,还支持其它界面库(如angular, vue 等)。 它体小精悍(只有2KB–包括依赖)。
使用时机
- 不需要使用时
-
如果你不知道是否需要 Redux,那就是不需要它
-
只有遇到 React 实在解决不了的问题,你才需要 Redux
- 简单说,如果你的UI层非常简单,没有很多互动,Redux 就是不必要的,用了反而增加复杂性。
-
- 需要使用时
- 若是组件间状态比较复杂,可以使用redux.
- 比如:某个组件的状态,需要共享
- 比如:某个状态需要在任何地方都可以拿到
- 比如: 一个组件需要改变全局状态
- 比如:一个组件需要改变另一个组件的状态
- 若是组件间状态比较复杂,可以使用redux.
redux基本概念
Store
Redux为单一
数据源。
整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一
一个 store 中。
将state、action、reducer 关联在一起;
State
在 Redux API 中,State指一个唯一的 state 值
- state 就是Redux 应用的全部状态 即
Redux管理的全部数据
,通常为一个多层嵌套的对象。 - state 由 store 管理
- 当想要获取state数据时 通过API -> getState 获取。
Action
Action 是一个普通对象
,用来表示即将改变 state 的意图。
State 是只读的,唯一改变 state 的方法就是触发 action。它是将数据放入 store 的唯一途径。
约定俗成,action 必须拥有一个 type
属性,表示动作类型,其他属性由自己决定。
reducer
Reducer 的作用是用于描述action 如何改变 state tree。
Reducer 只是一些纯函数
,它接收先前的 state 和 action,并返回新的 state
可用于初始化状态、加工状态;
redux工作原理
从原理图来看 compoents 、Action Creators、 Reducers、Stroe -> 表示组件、action 、reducer都是存在多个,而Stroe是唯一的。
若是仅存在一个组件,根本不需要使用redux,只需要使用组件内部状态管理就可以实现功能。
所以现在假设存在多个组件,组件的数据使用 redux 统一管理。当某个组件想要修改数据时
- [1] 组件想要修改数据 -> 告诉 Action创建者
- [2] Action 创建者去创建一个Action动作对象(里面包含干什么、如何干)
- [3] 通过 dispatch函数将 Action动作对象 派发出去
- [4] Store(调度者) 接收到action,将action和preState(原来的值)交给 reducer
- [5] reducer 进行计算 并将计算得到的值 交给 Store
- [6] 组件想要获取数据时 再通过 getState方法 获取更新后的值
举一个最简单的例子
- component:客人
- Action Creators : 服务员
- dispatch: 点菜宝
- Store: 老板
- reducer:厨师
语法
[1] 创建Store
createStore
combineReducers
Store 是通过 createStore
方法创建的
- (1) 引入createStore
import { createStore } from 'redux'
- (2) 创建Store
createStore( reducer )
- 若是仅仅存在一个reducer,我们可以直接引入(值为一个函数)
此时若是获取state, 得到的值就是 该reducer返回的值,比如numReducer返回值为0,此时通过getState得到的值就是0。import { createStore } from 'redux' import numReducer from './numReducer' const store = createStore( numReducer )
- 若是存在多个reducer, 就需要通过
combineReducers
方法进行reducer合并
此时若是获取state, 得到的值就是 一个对象import { createStore, combineReducers } from 'redux' import numReduce from './numReduce' import countReduce from './countReduce' const reduces = { num:numReduce, count:countReduce } const store = createStore( combineReducers(reduces) )
{ num:numReduce函数的返回值, count:countReduce函数的返回值 }
- 若是仅仅存在一个reducer,我们可以直接引入(值为一个函数)
[2]创建并分发action
dispatch
applyMiddleware
使用Action Creator 去创建Action,使用dispatch
去派发action。
语法
action的值可以是一个对象,也可以是一个函数
- 若是值为一个
对象
表示是同步
更新数据{ type: 动作指令(String类型), data: 操作数据 }
- 若是值为一个
函数
表示是异步
更新数据// 该方法默认会将dispatch作为函数传入 (dispatch)=>{ setTimeOut(()=>{ dispatch({type:动作指令,data: 操作数据}) },0) // 使用定时器模拟异步操作 }
举例说明- 做一个加法运算
- 同步加法
- action
export const addAction = data => ({type:INCREMENT,data})
- 在组件中派发此action
import {addAction} from '../../../store/action.js import store from '../../../store' // 派发 store.dispatch(addAction( value ))'
- action
- 异步加法
- action
export const addActionAsync = (data, time) =>{ return (dispatch)=>{ setTimeout(()=>{ dispatch({type:'add',data}) },time) } }
- 在组件中派发action
import {addActionAsync} from '../../../store/action.js import store from '../../../store' // 派发 store.dispatch(addActionAsync( value ))'
- action
执行原理
当使用dispatch去派发action时,在Store会接收到-> Stroe会去查看action的类型
-
若是类型为一个对象 -> Store会将action交给 reducer
-
若是类型为一个函数 -> 会报错
希望得到的值为一个对象。
若是希望action能够兼容值为函数的情况(也就是能够使用异步Action),需要使用一个中间件redux-thunk
-
使用redux-thunk中间件后,若是类型为一个函数 -> store会调用该函数并将dispatch 方法作为参数传入(而不会交给reducer)
redux-thunk中间件使用
- 作用:可以让sotre识别 函数类型的action
- 使用
- [1] 下载
使用命令npm i redux-thunk
下载 - [2] 配置
因为我们的目的是想让Stroe能够识别 函数类型的action,所以在store创建过程进行配置 -> 使用applyMiddleware
配置中间件import { createStore, applyMiddleware } from 'redux' const store = createStore( // 合并 combineReducers(reducers), // 中间件 applyMiddleware(Thunk) )
- [1] 下载
[3]创建reducer
语法
const init = 初始化的值 // 若是不设置,第一次渲染时默认为undefined
export default function( preState=init, action ){
const {type, data} = action
switch (type){
case type值匹配:
// 匹配后的逻辑处理
return 新的state值
...
default:
return preState
}
}
渲染过程
- 在第一次进入页面时 会默认调用所有的reducer, 此时传入的 preState值为
默认传入的action值为undefined
{ type:@@init... // 后面拼接随机字符 }
- 后面每次通过dispatch派发action时都会调用对应的reducer
[4]getState
作用:用于获取数据
语法
import { createStore } from 'redux'
const store = createStore(reducer)
store.getState() // 获取数据
[5]subscribe
作用:用于监听redux中状态的变化,只要变化,就会走subscribe函数;
原因: redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己来;
语法
import { createStore } from 'redux'
const store = createStore(reducer)
store.subscribe(()=>{
console.log('逻辑代码-> 更新数据')
})
举例说明
下面示例的编写的前提是了解redux的API的基础之上,若是没有了解过,可以结合下方的 API一起看;
现在有如上示例
- 第一个:num的值;
- 第二个:操作数,可以选择为1、2、3
- 第三个:按钮,点击num的值变为 当前值+操作数
- 第四个:按钮,点击num的值变为 当前值-操作数
- 第五个:按钮,点击若是当前num的值为偶数->无变化;若是当前num的值为奇数-> num的值变为 当前值+操作数;
- 第六个:按钮,点击异步增加num的值
若是值存储在当前组件的state中,代码如下
import React, { Component } from 'react'
export default class New extends Component {
state = { num: 0 }
render() {
const { num } = this.state
return (
<div>
{num}
<select ref={this.getref}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<button onClick={()=>{this.calculate('increment')}}>+</button>
<button onClick={()=>{this.calculate('decrement')}}>-</button>
<button onClick={()=>{this.calculate('incrementIfOdd')}}>和为基数再加</button>
<button onClick={()=>{this.calculate('incrementAsync')}}>异步加</button>
</div>
)
}
getref = (dom) => {
this.select = dom
}
calculate = (type)=>{
// 获取到select选择的值
const { value } = this.select
const { num } = this.state
switch (type){
case 'increment':
this.setState({num: num + +value})
break;
case 'decrement':
this.setState({num: num - +value})
break;
case "incrementIfOdd":
if(num % 2 != 0){
this.setState({num: num + +value})
}
break;
case 'incrementAsync':
setTimeout(()=>{
this.setState({num: num + +value})
}, 100)
}
}
}
配置redux
[1] 下载
使用命令npm install --save redux
下载redux
[2] 配置
- (1)在src文件夹下创建一个store文件夹
- (2) 在store文件夹下创建一个index.js文件 -> 创建store并导出
import { createStore ,combineReducers,applyMiddleware} from 'redux' import reducers from './reduces' import Thunk from 'redux-thunk' export default createStore( combineReducers(reducers), // 合并reducer applyMiddleware(Thunk) // 配置中间件 )
- (3) 在store 文件夹下创建一个reducers文件夹(因为reducer可以有多个),有多少个reducer就在reducers文件夹下创建多少个文件
- num的reducer -> num.js
// num默认值为0 import {INCREMENT, DECREMENT} from '../static' export default function( preState=0, action ){ const {type, data} = action switch (type){ case INCREMENT: return preState + data case DECREMENT: return preState - data default: return preState // 初始化的时候type为 @@init... } }
- 集成到reduces文件夹下的index.js文件中
// 将所有定义的数据发导入到这个对象中然后返回 import numReduce from './num' const reducers = { num: numReduce // 这个值将会作为创建的state中数据的属性名‘num’ }; export default reducers;
- num的reducer -> num.js
- (4)在store 文件夹下创建一个actions文件夹(因为action可以有多个),有多少组action就在actions文件夹下创建多少个文件
- 有关num的action -> num.js
// action可以是一个对象 也可以是一个函数 // 若是action 为一个对象表示同步更新数据,action为一个函数表示异步更新数据 import {INCREMENT, DECREMENT} from '../static' export const addAction = data => ({type:INCREMENT,data}) export const decreaseAction = data => ({type:DECREMENT,data}) export const addActionAsync = (data, time) =>{ return (dispatch)=>{ setTimeout(()=>{ dispatch(addAction(data)) },time) } }
- 集成action到actions文件夹下的index.js中
import { addAction, decreaseAction, addActionAsync } from './numAction' export { addAction, decreaseAction, addActionAsync }
- 有关num的action -> num.js
- (5)创建static.js文件 -> 用于保存action的
动作指令
// 指令 export const INCREMENT = 'increment' // 加法运算 export const DECREMENT = 'decrement' // 减法运算
react-redux
由于很多开发者都喜欢在react中使用redux, Facebook就开发了 react-redux。
react-redux将组件分为 容器组件
和UI组件
- UI组件只负责页面的呈现,交互等;在UI组件中不能使用任何关于Redux的API;
- 容器组件是真正与Redux打交道的,里面可以随意使用Redux的API,最终将结果交给UI组件;
- 所有的UI组件都应该包裹一个容器组件,他们是父子关系;
- 容器组件是连接 UI组件 与 Redux的桥梁,容器组件会传递给UI 组件
- Redux中所保留的状态;
- 用于操作Redux的方法;
语法
connect
connect用于创建一个容器组件
import {connect} from 'react-redux';
export default connect(function1,function2)(UI组件)
-
connect为一个函数
connect( state=> { return 对象 // return的对象的key ,value将添加在UI组件的props中 }, dispatch =>{ return 对象 // return的对象的key ,value将添加在UI组件的props中 } )
参数1(mapStateToProps): 是一个回调函数
- 该函数是 react-redux 调用,该函数在调用时会将state作为参数传入
- 函数的返回值为一个对象 ,作为UI组件接收的props- 状态
参数2(mapDispatchToProps): 是一个回调函数
- 该函数是 react-redux 调用,在调用时会将dispatch 方法作为参数传入
- 函数的返回值为一个对象,作为UI组件接收的props- 操作状态的方法
返回值:connect函数的返回值也是一个函数
- 调用时将 UI组件 作为参数传入
- 最终得到的值是一个容器组件
注意点:在使用容器组件时需要将 store作为prop传入
<Count stroe={ store }/>
在UI组件中接收-> 在props中除了接收 connect中接收的两个方法的返回值还有默认的store的属性和方法
Provider组件
若是我们有多个组件 ,每次都需要在容器组件传递 store,而且若是组件不是普通组件而是路由组件还不好传递,此时就可以使用Provider
组件
// [1] 引入组件
import { Provider } from 'react-redux'
import store from './store'
// [2] 包裹跟组件
<Provider store={store}>
<App/>
</Provider>
Provide组件会去查看项目里哪些组件为容器组件 将 store 传递
监听
redux是 redux只负责管理状态,至于状态的改变驱动着页面的展示是需要我们自己控制的
若是使用了react-redux,就不需要自己监听了,当状态改变时会自动渲染对应的UI组件
配置
[1]下载
使用命令 npm i react-redux
下载react-redux;
[2]配置
对于上面例子的改进
import React, { Component } from 'react'
import {addAction, decreaseAction, addActionAsync} from '../../../store/actions/index'
import {connect} from 'react-redux'
class New extends Component {
render() {
const { numReduce } = this.props
return (
<div>
{numReduce}
<select ref={this.getref}>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
<button onClick={()=>{this.calculate('increment')}}>+</button>
<button onClick={()=>{this.calculate('decrement')}}>-</button>
<button onClick={()=>{this.calculate('incrementIfOdd')}}>和为基数再加</button>
<button onClick={()=>{this.calculate('incrementAsync')}}>异步加</button>
</div>
)
}
getref = (dom) => {
this.select = dom
}
calculate = (type)=>{
// 获取到select选择的值
const { value } = this.select
const { numReduce, add, decrease, addAsync} = this.props
switch (type){
case 'increment':
add( +value )
break;
case 'decrement':
decrease( +value )
break;
case "incrementIfOdd":
if(numReduce % 2 != 0){
add( +value )
}
break;
case 'incrementAsync':
addAsync( +value , 2000 )
}
}
}
export default connect(
state => {
return state
},
dispatch => {
return {
add(value){
dispatch(addAction( value ))
},
decrease(value){
dispatch(decreaseAction(value))
},
addAsync(value,time){
dispatch(addActionAsync(+value, time))
}
}
}
)(New)