Redux基本介绍
redux
Redux 是 JavaScript 应用的状态容器,提供可预测的状态管理。
当遇到如下问题时,建议开始使用 Redux:
- 你有很多数据随时间而变化
- 你希望状态有一个唯一确定的来源
- 你发现将所有状态放在顶层组件中管理已不可维护
redux和react的关系
Redux并不只为react应用提供状态管理, 它还支持其它的框架。
为什么Rect要用 Redux
React 是 DOM 的一个抽象层(UI 库),并不是 Web 应用的完整解决方案。因此react在涉及到数据的处理以及组件之间的通信时会比较复杂。
对于大型的复杂应用来说,这两方面恰恰是最关键的。因此,只用 React,写大型应用比较吃力。
- 2014 年 Facebook 提出了 Flux 架构的概念,引发了很多的实现。
- 2015 年,Redux 出现,将 Flux 与函数式编程(reducer)结合一起,很短时间内就成为了最热门的前端架构。
- Flux 是最早的状态管理 工具,它提供了状态管理的思想,也提供对应的实现
- 除了 Flux、Redux 之外,还有:Mobx 等状态管理工具
-
主要的区别:组件之间的通讯问题
-
不使用 Redux (图左边) :
- 只能使用父子组件通讯、状态提升等 React 自带机制
- 处理远房亲戚(非父子)关系的组件通讯时乏力
- 组件之间的数据流混乱,出现 Bug 时难定位
-
使用 Redux (图右边):
- 集中式存储和管理应用的状态
- 处理组件通讯问题时,无视组件之间的层级关系
- 简化大型复杂应用中组件之间的通讯问题
- 数据流清晰,易于定位 Bug
Redux 三个核心概念
理解三个核心概念
核心概念:store、action、reducer
action
-
动作。
-
一个js对象,包含两个属性:
- type: 标识属性,值是字符串。多个type用action分开
- payload:数据属性,可选。表示本次动作携带的数据
-
actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。
-
特点:
- 只描述做什么
- JS 对象,必须带有
type属性,用于区分动作的类型 - 根据功能的不同,可以携带额外的数据,配合该数据来完成相应功能
reducer
-
一个纯函数
-
作用
-
- 初始化状态
- 修改状态
-
-
修改状态
根据传入的旧状态和action,返回新状态
公式:
(previousState, action) => newState
store
-
store:仓库,Redux 的核心,整合 action 和 reducer
-
特点:
- 一个应用只有一个 store
- 维护应用的状态,获取状态:
store.getState() - 创建 store 时接收 reducer 作为参数:
const store = createStore(reducer) - 发起状态更新时,需要分发 action:
store.dispatch(action)
-
其他 API, — 订阅(监听)状态变化:
const unSubscribe = store.subscribe(() => {})— 取消订阅状态变化:unSubscribe()
安装
npm i redux
Redux功能演示核心代码
定义reuder
initState = 0
function reducer(state = initState, action) {
return state
}
定义action
const action1 = { type:'addN', payload: 12 }
// store.dispatch(action1)
const action2 = { type:'add', payload: 1 }
定义store
import { createStore } from 'redux'
// 创建 store
const store = createStore(reducer)
store 相关API
- store获取状态
store.getState() - store修改数据,通过派发action。
store.dispatch({type:xxx, payload: xx}}) - store添加订阅者。
// 格式:
// 取消订阅 = store.subscribe(订阅者)
// 订阅者:就是一个函数,当state值变化时,store会执行它
// store.subscript()的返回值也是一个函数,执行它时,它的
const unSubscribe = store.subscribe(() => {
// 状态改变时,执行相应操作
})
// 取消监听状态变化
// unSubscribe()
4.取消订阅者。unSubscribe()
核心代码
import { createStore } from 'redux'
initState = 0
function reducer(state = initState, action) {
return state
}
// 创建 store
const store = createStore(reducer)
// store获取状态
console.log(store.getState())
// 更新状态
// 1. dispatch 派遣,派出。表示:分发一个 action,也就是发起状态更新
// 2. store.dispatch会 调用reducer函数,并将action传递给reducer
const action1 = { type:'addN', payload: 12 }
store.dispatch(action1)
const action2 = { type:'add', payload: 1 }
store.dispatch(action2)
// store 添加订阅者
// store.subscribe
// 订阅者:就是一个函数,当state值变化时,store会执行它
const unSubscribe = store.subscribe(() => {
// 状态改变时,执行相应操作
console.log('数据变化了....')
})
// 取消监听状态变化
unSubscribe()
Redux 代码执行过程
获取默认值
只要创建 store,那么,Redux 就会调用一次 reducer, 且type是一个随机值。如下是一个示例:
- type是随机值确保了它不会被用户业务逻辑处理,而只能去匹配默认值。
这一次调用 reducer 的目的:获取状态的默认值。这个初始值将成为下一次调用 store.getState() 方法来获取 Redux 状态值的preState
更新状态
- 当你需要更新状态时,就先分发动作
store.dispatch(action) - Redux 内部,store 就会调用 reducer,传入:上一次的状态(当前示例中就是:
0)和 action({ type: 'add' }),计算出新的状态,并返回这个新值。 - reducer 执行完毕后,将最新的状态交给 store,store 用最新的状态替换旧状态,状态更新完毕
import { createStore } from 'redux'
const store = createStore(reducer)
function reducer(state = 0, action) {
console.log('reducer:', state, action)
switch (action.type) {
case 'add':
return state + 1
default:
return state
}
}
console.log('状态值为:', store.getState()) // 10
// 发起更新状态:
// 参数: action 对象
store.dispatch({ type: 'increment' })
// 相当于: reducer(10, { type: 'increment' })
console.log('更新后:', store.getState()) // 11
React-redux基本介绍
react中直接使用redux有很多不方便的地方
- 每个组件都需要单独导入store
- 在根组件上的写法不友好
react-redux 库
是 Redux 官方提供的 React 绑定库
- React 和 Redux 是两个独立的库,两者之间职责独立。
- Redux可以和其他的js库,框架一起使用,而并不专门用于react。
- 为了实现在 React 中使用 Redux 进行状态管理 ,就需要一种机制,将这两个独立的库关联在一起。这时候就用到 React-Redux 这个绑定库了
- 作用: 为 React 接入 Redux,实现在 React 中使用 Redux 进行状态管理。
react-redux-基本使用
步骤
-
安装
npm i react-redux -
使用
- 按redux的要求,创建好store, reducer,action等等
- 从react-redux中引入 provider, useSelector, useDispatch来 操作 redux
API
Provider
- 用法:直接包装在根组件上。
<Provider store={store}> - 好处:相比react + redux,这样就不需要每个组件都引入store了
useSelector
-
用法:获取公共状态
-
好处:
- 相比react + redux,不需要使用store.getState()了
- state变化了,它会自动更新
-
格式:
const 状态 = useSelector(store的数据 => 你需要的部分)
useDispatch
- 用法:派发action,修改数据
- 格式:
const dispatch = useDispatch(); dispatch(action)
action creator
问题导入
dispatch({type: 'book/add', payload: '三国演义'})
dispatch({type: 'book/add', payload: '红楼梦'})
一个action的type名字出现多次,多次dispatch会重复,不利于修改。
思路
补充一个action creator。
- 它是一个函数。
- 作用:用来创建指定类型的action。
- 入参:payload
- 返回值: action
- 示例:
const addAction = (payload) => ({type: 'book/add', payload})
dispatch(addAction('三国演义'))
dispatch(addAction('红楼梦'))
Action Type的使用
问题
背景:在reducer和action creator中都用到了一个字符串的 action type。
问题:容易写错,不利于统一修改。
解决
思路:集中处理 action type,保持项目中 action type 的一致性
处理方式:
- 在 store 目录中创建
actionTypes目录或者constants目录,集中处理 - 使用常量来存储 action type。例如
export const SET_NAME = 'user/setName'
export const SUB_MORE = 'money/subMore'
3.将项目中用到 action type 的地方替换为这些常量,从 而保持项目中 action type 的一致性
redux-中间件
方案
方案1:在业务组件中发请求,拿到数据之后,再dispath
方案2:在业务组件中直接dispatch,在dispatch中发请求,然后保存redux。
中间件
中间件:middleware。用来在不损害原功能的前提下,引入额外的代码来拓展功能。 Redux 中间件
- 中间:在 dispatch action 和 到达 reducer 之间
图示
没有中间件:
dispatch(action) => reducer。用来发起状态更新
使用中间件:
dispatch(action) => 执行中间件代码 => reducer。- dispatch() 就是 中间件 封装处理后的 dispatch,但是,最终一定会调用 Redux 库自己提供的 dispatch 方法
redux-中间件-redux-thunk
作用
redux-thunk 中间件允许redux处理函数形式的 action。在函数形式的 action 中就可以执行异步操作代码,完成异步操作。
之前
const action1 = {type: 'todos/add', payload: '学习redux'}
dispatch(action1)
之后
const action1 = async (dispatch) =>{
const res = await 异步动作()
dispatch({type: 'todos/add', payload: '学习redux'})
}
dispatch(action1)
步骤
-
安装:
npm i redux-thunk -
使用:在store/index.js
- 导入 redux-thunk , applyMiddleware
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
2.调用applyMiddleware将 thunk 添加到中间件列表中
const store = createStore(rootReducer, applyMiddleware(thunk))
3.修改 action creator,返回一个函数,其形参就是redux的dispatch
const addTodo = (name)=> {
return async (dispatch) =>{
const res = await 异步动作()
dispatch({type: 'todos/add', payload: name})
}
}
dispatch(addTodo('学习redux'))
redux-中间件-redux-logger
步骤
-
安装:
npm i redux-logger -
使用。store/index.js
- 导入 redux-logger
- 调用 applyMiddleware 函数时,将 logger 作为参数传入
import { createStore, applyMiddleware } from 'redux'
import logger from 'redux-logger'
import rootReducer from './reducers'
const store = createStore(rootReducer, applyMiddleware(logger))
3.测试效果
任意调用 store.dispatch() 查看 logger 中间件记录的日志信息
redux-devtools-extension的使用
redux-devtools-extension
方便在浏览器中调试redux操作的工具
先要安装 redux的开发者工具,再安装redux调试工具
步骤
- 安装react开发者工具(chrome浏览器插件)
- 安装redux的开发者工具(chrome浏览器插件)
- 在项目中安装redux调试工具,它是一个npm包。
npm i redux-devtools-extension -D - 配置。在store/index.js中进行配置和导入
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools(applyMiddleware(中间件..)))
export default store
redux-thunk-中间件原理
redux-thunk的源码非常简单
注意下面的typeof action === 'function'
function createThunkMiddleware(extraArgument) {
// Redux 中间件的写法:const myMiddleware = store => next => action => { /* 此处写 中间件 的代码 */ }
return ({ dispatch, getState }) => (next) => (action) => {
// redux-thunk 的核心代码:
// 判断 action 的类型是不是函数
// 如果是函数,就调用该函数(action),并且传入了 dispatch 和 getState
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
// 如果不是函数,就调用下一个中间件(next),将 action 传递过去
// 如果没有其他中间件,那么,此处的 next 指的就是:Redux 自己的 dispatch 方法
return next(action);
};
}
宝,你都看到这了不给我一个star嘛?
PS: 如果内容有错误的地方欢迎指出(觉得看着不理解不舒服想吐槽也完全没问题);如果有帮助,欢迎点赞和收藏,转载请著明出处,如果有问题也欢迎私信交流