redux & react-redux初探
由 Vue 转 React,耕耘下 redux 笔记
redux 中文官方文档 > react-redux API 文档
建议写个 todoList 来熟悉新技术:可 clone 刚写的 todolist 。
一张图说清 React-Redux ,Redux , React 三者到底是什么关系。
- Redux: 首先 Redux 是一个应用状态管理 js 库,它本身和 React 是没有关系的,换句话说,Redux 可以应用于其他框架构建的前端应用,甚至也可以应用于 Vue 中。
- React-Redux:React-Redux 是连接 React 应用和 Redux 状态管理的桥梁。React-redux 主要专注两件事,一是如何向 React 应用中注入 redux 中的 Store ,二是如何根据 Store 的改变,把消息派发给应用中需要状态的每一个组件。
- React :就不说了
redux 篇
以 todolist 为例,redux 在使用中大致表现为 UI 组件、action、constants、reduces 四大部分:
下面开始分别介绍,大致流程为:首先在 constants 设置 type 参数,接着 action 声明要修改的对象类型(补上 type 参数),然后在 reduce(接收旧的 state 和 action 两个参数,返回新的 state)写修改数据的逻辑,最后在 UI 组件触发逻辑。
Contents:设置 Action 需要的 type 参数字段,Reduce 会依据这字段做不同逻辑
1 | export const TODO_DEL = 'TODO_DEL' |
Action:只描述 state 的变化而不更新
在 Redux 中,action 本质是一个 JavaScript 普通对象,可以理解为 store 数据的载体。
官网说:唯一改变 state 的方法就是触发 action,那是因为在 UI 组件中,dispatch(todoDel (!nowDone));
dispatch 了 action 暴露出来的 todoDel ,然后这个 todoDel 执行了 reduce 逻辑,然后发生了 state 改变。
1 | export const todoDel = (id) => ({ |
在 UI 组件里,只需把 action 创建的结果传给 dispatch()
方法,即可发起一次修改或更新数据(前提是 reduce 里写了逻辑)。
1 | dispatch(todoDel(text)) |
store 里能直接通过 store.dispatch()
调用 dispatch()
方法,但多数情况下会使用 react-redux 提供的 connect()
来调用。bindActionCreators()
可以自动把多个 action 创建函数绑定到 dispatch()
方法上。
Reducer:根据 Action 变化更新 State
Reducers :就是纯函数,它接收旧的 state 和 action 两个参数,返回新的 state 的函数。且依据应用状态的变化响应 actions 并将数据更新。
注意每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state
参数都不同,分别对应它管理的那部分 state 数据。
1 | //TODO_DEL:依据type参数,处理不同的action逻辑 |
永远不要在 reducer 里做这些操作:
- 修改传入参数;
- 执行有副作用的操作,如 API 请求和路由跳转;
- 调用非纯函数,如
Date.now()
或Math.random()
。 - 使用
combineReducers()
将多个 reducer 合并成为一个
谨记 reducer 一定要保持纯净。只要传入参数相同,返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用,没有 API 请求、没有变量修改,单纯执行计算。
UI 组件
上面一套流程走完后,就可以在 UI 组件触发事件,更新数据了。
1 | import { useDispatch } from 'react-redux' |
Store:将它们联系到一起的对象
创建一个 Redux store 来以存放应用中所有的 state:
createStore(reducer, [preloadedState], enhancer)
- reducer:接收旧的 state 和 action,返回新的 state 树
Store 有以下职责:
- 维持应用的 state;
- 提供
getState()
方法获取 state; - 提供
dispatch(action)
方法更新 state; - 通过
subscribe(listener)
注册监听器; - 通过
subscribe(listener)
返回的函数注销监听器。
发起 Actions
1 | // 打印初始状态 |
数据流
Redux 应用中数据的生命周期遵循下面 4 个步骤:
- 调用 Action,可以在任何地方
store.dispatch(action)
。
1 | //Action 就是一个描述“发生了什么”的普通对象 |
- store 将(当前的 state 树和 action) 传入 reducer 函数():
1 | // 当前应用的 state |
- 根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树
Redux 原生提供combineReducers()
辅助函数,来把根 reducer 拆分成多个函数,用于分别处理 state 树的一个分支。
1 | //假如有两个 reducer |
当你触发 action 后,combineReducers
返回的 todoApp
会负责调用两个 reducer:
1 | let nextA = A(state.A, action) |
然后会把两个结果集合并成一个 state 树:
1 | return { |
- Redux store 保存了根 reducer 返回的完整 state 树
这个新的树就是应用的下一个 state!所有订阅 store.subscribe(listener)
的监听器都将被调用;监听器里可以调用 store.getState()
获得当前 state。
React Redux 应该调用 component.setState(newState)
来更新。
一些 Api
useSelector
selector 回调函数会把 storeState 返回给你 你再进行筛选返回自己想要使用的数据
1 | const num = useSelector((state) => state.num) |
createStore
通过 createStore 将 state 存入 store
1 | const store = createStore(reducer, initialState) |
再通过 Provider 向子组件暴露 store,通过 store 在父子组件之间共享状态
1 | <Provider store={store}> |
useDispatch
通过useDispatch
可以获取 dispatch,用来提交更新
1 | import { useDispatch } from 'react-redux' |
context :createContext -> useContext
一种组件传值方式,能轻松拿到组件值。官方 Hooks 的 API
1 | //null可以为父组件要传递的值,不一定是null |
useEffect(callback,[])
React 会在每次渲染完后调用 useEffect,包括第一次加载渲染 DOM
接受两个参数,一个处理函数,另一个关联的状态或数组,这个变了就重新执行
useCallback + useMemo
他两个使用和 useEffect 差不多
解决的痛点:在函数组件中,定义在组件内的函数会随着状态更新而重新渲染,这样会影响的子组件频繁定义、渲染。
1 | //父组件 |
采用 useCallback +Memo 后:
1 | //使用Memo后确实不会影响了,但父组件传值过来呢 |