认识 Redux
为什么需要 Redux
- JavaScript 开发的应用程序,已经变得越来越复杂了:
- JavaScript 需要管理的状态越来越多,越来越复杂;
- 这些状态包括服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些 UI 的状态,比如某些元素是否被选中,是否显示加载动效,当前分页;
- 管理不断变化的 state 是非常困难的:
- 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View 页面也有可能会引起状态的变化;
- 当应用程序复杂时,state 在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常难以控制和追踪;
- React 是在视图层解决了 DOM 的渲染过程,但是 State 依然是需要自己来管理:
- 无论是组件定义自己的 state,还是组件之间的通信通过 props 进行传递;也包括通过 Context 进行数据之间的共享;
- React 主要负责帮助我们管理视图,state 如何维护最终还是我们自己来决定;
- Redux 就是一个帮助管理 State 的容器:Redux 是 JavaScript 的状态容器,提供了可预测的状态管理;
Redux 基本概念
State:应用程序中存储数据的地方,它是一个对象,包含整个应用程序的状态;
Action:定义应用程序中发生的操作的对象,它是一个简单的 JavaScript 对象,包含一个 type 属性和一些其他数据;
Reducer:Redux 中管理 state 的函数,每个 reducer 负责处理一个特定的部分的 state,并返回一个新的 state;
Store:Redux 中的核心对象,它负责保存整个应用程序的 state,并提供一些方法来访问和更新 state;
Redux 三大原则
单一数据源:整个应用程序的 state 被存储在一颗 object tree 中,并且这个 object tree 只存储在一个 store 中:
- Redux 并没有强制不能创建多个 Store,但是那样做并不利于数据的维护;
- 单一的数据源可以让整个应用程序的 state 变得方便维护、追踪、修改;
State 是只读的:唯一修改 State 的方法一定是触发 action,不要试图在其他地方通过任何的方式来修改 State:
- 这样就确保了 View 或网络请求都不能直接修改 state,它们只能通过 action 来描述自己想要如何修改 state;
- 这样可以保证所有的修改都被集中化处理,并且按照严格的顺序来执行,所以不需要担心 race condition (竟态) 的问题;
使用纯函数来执行修改:通过 reducer 将旧 state 和 actions 联系在一起,并且返回一个新的 State:
- 随着应用程序的复杂度增加,可以将 reducer 拆分成多个小的 reducers,分别操作不同 state tree 的一部分;
- 但是所有的 reducer 都应该是纯函数,不能产生任何的副作用;
Redux 基本使用
Redux 结构划分
-
安装 redux
npm install redux --save
-
创建一个新的项目文件夹:learn-redux
npm init
-
修改 package.json 可以执行 index.js
"scripts": { "start": "node src/index.js" }
-
对 redux 结构进行划分
JavaScriptJavaScriptJavaScriptJavaScriptJavaScript// store/index.js import * as redux from "redux"; import reducer from "./reducer.js"; const store = redux.createStore(reducer); export default store;
// store/actionCreators.js import { ADD_NUMBER, SUB_NUMBER } from "./constants.js"; const addAction = (count) => ({ type: ADD_NUMBER, num: count, }); const subAction = (count) => ({ type: SUB_NUMBER, num: count, }); export { addAction, subAction };
// store/reducer.js import { ADD_NUMBER, SUB_NUMBER } from "./constants.js"; const initialState = { counter: 0, }; function reducer(state = initialState, action) { switch (action.type) { case ADD_NUMBER: return { ...state, counter: state.counter + action.num }; case SUB_NUMBER: return { ...state, counter: state.counter - action.num }; default: return state; } } export default reducer;
// store/constants.js const ADD_NUMBER = "ADD_NUMBER"; const SUB_NUMBER = "SUB_NUMBER"; export { ADD_NUMBER, SUB_NUMBER };
// src/index.js 测试 import store from "./store/index.js"; import { addAction, subAction } from "./store/actionCreators.js" // 订阅store的变化,当状态更新时,会调用这个函数 store.subscribe(() => { console.log(store.getState()); }); // 发起一个 action 更新状态 store.dispatch(addAction(1)); // 输出:1 store.dispatch(addAction(1)); // 输出:2 store.dispatch(subAction(1)); // 输出:1
Redux 流程图
-
全局通常只有一个 Store 存储 State;
-
Component 中在某些情况会派发 Action (这些 Action 是提前定义好的);
-
Reducer 会接收到这些 Action,并且在 Reducer 中会返回一个新的 State,作为 Store 的 State;
-
State 发生更新之后会触发通知,告知订阅者数据发生了改变;
-
订阅者拿到最新的数据 (在 props 中),更新到 jsx 中,界面发生改变;
React✍️ 生命周期
上一篇