redux
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。官网
安装redux
1 2 3
| npm install --save redux 或 yarn add redux
|
redux三大核心属性
action
Action
本质上是 JavaScript 普通对象
。我们约定,action 内必须使用一个字符串类型的 type
字段来表示将要执行的动作。多数情况下,type 会被定义成字符串常量。
1 2
| const add = (payload) => ({ type: 'add', payload })
|
reducer
reducer
就是一个纯函数
,接收旧的 state 和 action,返回新的 state。
注意 reducer 是纯函数。它仅仅用于计算下一个 state。它应该是完全可预测的:多次传入相同的输入必须产生相同的输出。它不应做有副作用的操作,如 API 调用或路由跳转。这些应该在 dispatch action 前发生。
下面代码就是reducer接收了一个旧的state和action,返回了新的state
1 2 3 4 5 6 7 8 9 10 11
| const reducer = (state, action) => { switch (action.type) { case 'add': return (state = state + action.payload) default: return state } } reducer(0,add(1))
|
store
在前面的代码中,我们学会了使用 action 来描述“发生了什么”,和使用 reducers 来根据 action 更新 state 的用法。
Store
就是把它们联系到一起的对象
。Store 有以下职责:
维持应用的 state;
提供 getState() 方法获取 state;
提供 dispatch(action) 方法更新 state;
通过 subscribe(listener) 注册监听器;
通过 subscribe(listener) 返回的函数注销监听器。
再次强调一下 Redux 应用只有一个单一
的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。
根据已有的 reducer 来创建 store
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import { configureStore } from '@reduxjs/toolkit'
const Add = (payload) => ({ type: 'add', payload })
const reducer = (state = 0, action) => { switch (action.type) { case 'add': return state + action.payload default: return state } }
const store = configureStore({ reducer })
const unsubscribe = store.subscribe(() => console.log('state:',store.getState()))
store.dispatch(Add(1)) store.dispatch(Add(2))
unsubscribe()
|
1 2 3 4 5
| const store = configureStore({ reducer:xxx, preloadedState:xxx, })
|
Redux获取状态默认值的执行过程
只要创建store,那么,Redux就会调用一次 reducer
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const Add = (payload) => ({ type: 'add', payload })
const reducer = (state, action) => { console.log('reducer',state,action); switch (action.type) { case 'add': return state + action.payload default: return state } }
const store = configureStore({reducer})
|
这一次调用renucer的目的:获取状态的默认值,
第一次调用reducer时,redux机制会让action.type的value是一个随机的字符串,以确保return的是state
redux内部第一次调用reducer:
1
| reducer undefined {type: '@@redux/INITr.5.x.p.5'}
|
redux数据流
严格的单向数据流是 Redux 架构的设计核心。
这意味着应用中所有的数据都遵循相同的生命周期,这样可以让应用变得更加可预测且容易理解。同时也鼓励做数据范式化,这样可以避免使用多个且独立的无法相互引用的重复数据。
React-redux
Provider
在项目入口文件,一般是src目录下的index.js,为整个项目(App组件)提供redux状态管理
1 2 3 4 5 6 7 8 9 10
| import ReactDOM from 'react-dom/client' import { Provider } from 'react-redux' import App from './App' import store from './store' const root = ReactDOM.createRoot(document.getElementById('root')) root.render( <Provider store={store}> <App /> </Provider> )
|
完整示例
获取个人信息的示例,完整的展示了redux的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { useDispatch, useSelector } from 'react-redux' import { getUserInfo } from '@/store/actions' export default function GeekLayout() { const dispatch = useDispatch() useEffect(() => { dispatch(getUserInfo()) }, [dispatch]) ... return( ... {name} ) }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { http } from '@/utils' export const getUserInfo = () => { return async (dispatch, getState) => { const { login: token } = getState() const res = await http.get('/v1_0/user/profile', { headers: { Authorization: `Bearer ${token}`, }, }) dispatch({ type: 'user/userInfo', payload: res.data.data }) } }
|
1 2 3 4 5 6 7 8 9 10
| const initialState = {} export const user = (state = initialState, action) => { switch (action.type) { case 'user/userInfo': return action.payload default: return state } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { useDispatch, useSelector } from 'react-redux' import { getUserInfo } from '@/store/actions' export default function GeekLayout() { const dispatch = useDispatch() useEffect(() => { dispatch(getUserInfo()) }, [dispatch]) const { name } = useSelector((state) => state.user) ... return( ... {name} ) }
|
reducer的分离与合并
根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。
根 reducer 的结构完全由你决定。Redux 原生提供combineReducers()
辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import {combineReducers} from 'redux' function todos(state = [], action) { return nextState }
function visibleTodoFilter(state = 'SHOW_ALL', action) { return nextState }
let todoApp = combineReducers({ todos, visibleTodoFilter })
|
当你触发 action 后,combineReducers
返回的 todoApp
会负责调用两个 reducer:
1 2
| let nextTodos = todos(state.todos, action) let nextVisibleTodoFilter = visibleTodoFilter(state.visibleTodoFilter, action)
|
然后会把两个结果集合并成一个 state 树:
1 2 3 4
| return { todos: nextTodos, visibleTodoFilter: nextVisibleTodoFilter }
|
action type or constants
为了解决action和reducer中字符串误写错和命名冲突,建议单独抽离放在 actiontypes或constants目录中,一般放在store目录下
目录结构
|– store
|– actions 负责对应组件的action
|– actiontypes 抽离字符串
|– reducers 负责对应组件的reducer
|– index.js 入口文件,创建唯一的store
1 2 3 4
| const add = 'counter/add' const sub = 'counter/sub' export { add, sub }
|