Redux
- 状态管理器
- 集中式管理应用中的状态数据,以一种可预测的方式更新状态数据
- react-redux 绑定库官网
redux 与 react 没有任何直接关系,所以在 react 中要使用 redux 进行状态管理,通常还需要额外的绑定库:react-redux
- store:仓库,是一个容器,主要用于集中管理应用中的状态
- state:状态,是各组件需要共享的状态数据
- action:是一个普通对象,通常用于描述发生了什么。这个对象中一般有两个属性:type 和 payload
- action creator:action 创建函数,是一个函数结构,主要用于创建 action 对象,以实现 action 的复用
- reducer:是用于同步更新状态数据的纯函数。该函数以 state 和 action 作为参数,返回新的 state 状态数据。函数签名是:(state, action) => newState
注:纯函数 返回值依赖于参数 函数主体内部不产生任何副作用
- 【react|react3-Redux】Reducer 必需符合以下规则:
- 仅使用 state 和 action 参数计算新的状态值
- 禁止直接修改 state。必须通过复制现有的 state 并对复制的值进行更改的方式来做 不可变更新(immutable updates)
- 可使用lodash来实现深拷贝
- 禁止任何异步逻辑、依赖随机值或导致其他“副作用”的代码
- reducer 函数内部的逻辑通常遵循以下步骤:
- 检查 reducer 是否关心这个 action
- 如果是,则复制 state,使用新值更新 state 副本,然后返回新 state,否则,返回原来的 state 不变
- dispatch:是一个函数,传递 action 作为参数,以调用到 reducer 进行状态更新(在组件中不能直接调用 reducer() 进行状态更新,注reducer() 函数的调用是封装在 dispatch() 函数体内部实现的。)
- selector:是一个函数,用于从 store 的 state 状态树中提取片段。
State 是只读的 唯一改变 state 的方法就是触发 action(即调用 dispatch(action) 方法),action 是一个用于描述已发生事件的普通对象
使用纯函数来执行修改 为了描述 action 如何改变 state tree ,你需要编写 reducers
使用
- 安装
- $ npm i redux@4.1.2 react-redux@7.2.6
- $ yarn add redux@4.1.2 react-redux@7.2.6
- 创建 reducer
- 创建.src/actions/ constants.js目录
/** action type 的取值 */
export const ADD_TO_CART = Symbol('add_to_cart')
- 创建 action creator
创建 ./src/actions/cart.js 目录,定义 action creator(action创建函数),如:
/**
* 定义 action creator (action创建函数),用于创建 action 对象,以便于复用 action 对象
*/import { ADD_TO_CART } from "./constants"/**
* 用于创建添加到购物车时使用的 action 对象
*/
export const addToCartAction = (product) => {
return {
type: ADD_TO_CART,
payload: product,
}
}
- 创建 ./src/reducers/cart.js 目录,定义 reducer 函数,如:
/**
* 实现购物车状态数据管理
*/import _ from 'lodash'
import { ADD_TO_CART, REMOVE_FROM_CART } from "../actions/constants"/**
* 初始状态数据
*/
const initialState = {
cart: [],
}/**
* reducer 函数,是一个纯函数,用于同步更新状态数据
* @param {*} state 旧状态数据
* @param {*} action action对象,有 type 和 payload 属性
* @returns 返回新的状态数据
*/
const cartReducer = (state = initialState, { type, payload }) => {/** 对 state 进行深克隆 */
const copyState = _.cloneDeep(state)/**
* 使用 switch 多分支选择,来判断当前 action 的动作类型
*/
switch (type) {
case ADD_TO_CART: // 添加购物车
copyState.cart.push(payload)
return copyState
case REMOVE_FROM_CART: // 从购物车中删除商品
copyState.cart = copyState.cart.filter(prod => prod.id !== payload.id)
return copyState
default:
return state
}
}export default cartReducer
- ./src/reducers/index.js将各独立的 reducer 合并为根 reducer,如:
/**
* 将多个独立的 reducer 合并为一个根 reducer
*/import { combineReducers } from 'redux'
import cartReducer from './cart'const rootReducer = combineReducers({
shoppingCart: cartReducer,
})export default rootReducer
- 创建 Store
创建 ./src/store 目录,定义 Store:
/**
* 创建 Store 仓库
*/import { createStore } from 'redux'
import rootReducer from '../reducers'/**
* 基于根 reducer 来创建 store
*/
const store = createStore(rootReducer)export default store
- 入口文件.src/index.js组件中连接 Redux 的 Store
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import {Provider} from "react-redux";
import store from "./store";
ReactDOM.render(
,
document.getElementById('root')
)
- 文件中使用
import React from 'react'
import {useDispatch, useSelector} from "react-redux";
// 引入并结构出useDispatch(使用方法,改变数据)useSelector(拿到数据)
impot {addToCartActio} fron "../actions/cart.js"function React() {
const dispatch=useDispatch()
const cart=useSelector(state=>state.shoppingCart.cart) // 格式一个回调函数,state.空间名字.仓库数据名字
const clickHandler=()=>{
dispatch(addToCartAction({id:0,title:'商品信息'})) // 触发的actions事件
}
return (
react
)
}
export default React
异步任务
- 处理异步任务需要三方包
- yarn add redux-thunk
- 在store中引入
import {createStore,applyMiddleware} from "redux";
import logger from 'redux-logger' // 记录仓库状态数据
import thunk from 'redux-thunk' // 异步仓库任务处理
import rootReducer from "../reducers/index"const store = createStore(rootReducer,applyMiddleware(logger,thunk))
export default store
- 在需要异步处理的任务中写法,如actions中登录网络请求
import {LOGIN_SUCCESS,LOGIN_FAILED,LOGIN_RESET} from '../constants/action-types'
import {postLogin} from '../api/user'export const loginSuccessAction=info=>{
return {
type:LOGIN_SUCCESS,
payload:info,
}
}
// 重置
export const loginResetAction=()=>{
return{
type: LOGIN_RESET,
}
}
// 异步请求登录网络
export const loginAsyncAction=(info)=>{
/**
* 返回的这个函数,会自动被 redux-thunk 中间件调用执行。
* 在调用执行这个函数时,会自动传递 dispatch 作为参数。
* 不要求返回的这个函数保持纯净(即可以包含异步逻辑)
*/
return async dispatch=>{
const {status,data}=await postLogin(info)
if(status===200){ // 登录成功
dispatch(loginSuccessAction(data))
return true
}else { // 登录失败
dispatch({
type:LOGIN_FAILED
})
return false
}
}}
推荐阅读
- 工具实例|react+react-beautiful-dnd实例代办事项
- java|职教云自动选题、答题并自动修改答题已用时间 脚本
- 网页设计|前端网页设计期末大作业-写真工作室网站(资源链接在文末)
- Vue|Vue核心?(生命周期)
- Vue|Vue中的过滤器
- Vue|Vue核心?(内置指令)
- WEB|我的VUE 学习之路(下)
- Axios|axios的配置对象说明(config)
- HTML + CSS + JavaScript 实现打地鼠小游戏 闲暇时刻玩一玩 轻松丢烦恼~