From 7f4c9960e94305e75f329d64b5010d9c5635bfd9 Mon Sep 17 00:00:00 2001 From: ZiuChen Date: Wed, 26 Apr 2023 18:26:00 +0800 Subject: [PATCH] update --- docs/.vitepress/config.ts | 11 +- docs/note/React.assets/redux-async-action.svg | 16 ++ docs/note/React.assets/redux-usage.svg | 16 ++ docs/note/React.md | 165 ++++++++++++++++++ 4 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 docs/note/React.assets/redux-async-action.svg create mode 100644 docs/note/React.assets/redux-usage.svg diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index b8f86643..d4bd0bca 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -69,10 +69,13 @@ export default defineConfig({ copyright: 'Copyright © 2019-present Ziu Chen' }, lastUpdatedText: 'Updated Date', - algolia: { - apiKey: 'b4fd296ea5e467b3ac4a582160ff3122', - indexName: 'ziuchenio', - appId: 'LFZ2CPWWUG' + search: { + provider: 'algolia', + options: { + appId: 'LFZ2CPWWUG', + apiKey: 'b4fd296ea5e467b3ac4a582160ff3122', + indexName: 'ziuchenio' + } } } }) diff --git a/docs/note/React.assets/redux-async-action.svg b/docs/note/React.assets/redux-async-action.svg new file mode 100644 index 00000000..bd6f4736 --- /dev/null +++ b/docs/note/React.assets/redux-async-action.svg @@ -0,0 +1,16 @@ + + + + + + + 组件 ComponentcomponentDidMount调用映射的方法dispatch actionsreducer 接收 action更新 Store 中的state发送异步网络请求Redux 内部 \ No newline at end of file diff --git a/docs/note/React.assets/redux-usage.svg b/docs/note/React.assets/redux-usage.svg new file mode 100644 index 00000000..4c584d75 --- /dev/null +++ b/docs/note/React.assets/redux-usage.svg @@ -0,0 +1,16 @@ + + + + + + + ComponentActionReducerCentral StoreSubscriptionDispatchReachUpdateTriggerStatetoProps \ No newline at end of file diff --git a/docs/note/React.md b/docs/note/React.md index 59a569b3..5b75bbc5 100644 --- a/docs/note/React.md +++ b/docs/note/React.md @@ -3874,6 +3874,8 @@ export default store ``` ::: +![redux-usage](./React.assets/redux-usage.svg) + ### 进一步封装 可以将耦合在一起的代码拆分到不同文件中 @@ -4110,4 +4112,167 @@ export default connect( - 为高阶组件传入映射目标组件,最后高阶组件返回一个新组件 - 新组件的props包含了来自Store中状态/dispatch的映射 +### 异步Action + +有些场景下,我们希望组件能够直接调用Store中的action来触发网络请求,并且获取到数据 + +但是dispatch只允许派发对象类型的Action,不能通过dispatch派发函数 + +可以通过中间件`redux-thunk`来对Redux做增强,让dispatch能够对函数进行派发 + +```bash +npm i redux-thunk +``` + +通过`applyMiddleware`引入`redux-thunk`这个中间件: + +::: code-group +```tsx [index.js] {2,3,6} +// store/index.js +import { createStore, applyMiddleware } from 'redux' +import thunk from 'redux-thunk' +import reducer from './reducer' + +const store = createStore(reducer, applyMiddleware(thunk)) + +export default store +``` +```tsx [actionFactory.js] +// actionFactory.js +export const fetchPostList = () => { + return (dispatch, getState) => { + fetch('https://jsonplaceholder.typicode.com/posts') + .then((res) => res.json()) + .then((res) => { + dispatch({ + type: actionType.FETCH_POST_LIST, + list: res + }) + }) + } +} +``` +```tsx [list.jsx] +// list.jsx +import React, { Component } from 'react' +import { connect } from 'react-redux' +import { fetchPostList } from './store/actionFactory' + +const mapStateToProps = (state) => ({ + list: state.list +}) + +const mapDispatchToProps = (dispatch) => ({ + fetchList: () => dispatch(fetchPostList()) +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)( + class Profile extends Component { + render() { + return ( +
+

List

+ + {this.props.list.length && ( + + )} +
+ ) + } + } +) +``` +::: + +- 这样就可以将网络请求的具体逻辑代码隐藏到Redux中 +- 将网络请求归于状态管理的一部分 +- 而不是书写在组件内,不利于维护,耦合度太高 + +`redux-thunk`是如何做到可以让我们发送异步请求的? + +- 默认情况下`dispatch(action)`的action必须为一个JS对象 +- `redux-thunk`允许我们传入一个函数作为`action` +- 函数会被调用,并且将`dispatch`函数和`getState`函数作为入参传递给这个函数action + - `dispatch` 允许我们在这之后再次派发`action` + - `getState` 允许我们之后的一些操作依赖原来的状态,可以获取到之前的状态 + +下图展示了从组件调用方法,触发patch到Redux接收patch、发送网络请求、更新state的全过程: + +![redux-async-action](./React.assets/redux-async-action.svg) + +### 拆分Store + +拆分Store带来的益处很多,便于多人协作、不同业务逻辑解耦等 + +在Redux中,拆分Store的本质是拆分不同的`reducer`函数,之前在使用`createStore`时,传入的就是`reducer`函数 + +之前的Store写法与用法: + +```tsx {8-9} +// store/index.js +import { createStore } from 'redux' +import reducer from './reducer' + +const store = createStore(reducer) + +// App.jsx +store.getState().count +store.getState().list +``` + +拆分Store后的写法与用法: + +```tsx {7-8,14-15} +// store/index.js +import { createStore, combineReducers } from 'redux' +import counterReducer from './counter' +import postListReducer from './postList' + +const reducer = combineReducers({ + counter: counterReducer, + postList: postListReducer +}) + +const store = createStore(reducer) + +// App.jsx +store.getState().counter.count +store.getState().postList.count +``` + +拆分为多个Reducer之后,需要首先`getState()`获取到整个状态树,随后指定获取到不同的模块中的状态 + +拆分后,不同模块下的文件是保持一致的: + +```sh +- store/ # Store根目录 + - index.js # 导出 store 位置 + - counter/ # Counter模块 + - actionFactory.js + - constants.js + - index.js # 统一导出 + - reducer.js + - postList/ # PostList模块 + - actionFactory.js + - constants.js + - index.js + - reducer.js + - ... +``` + +#### combineReducer函数 + +前面拆分Store时用到了`combineReducer`函数,将多个模块reducer组合到一起,函数内部是如何处理的? + +- 将传入的reducers合并到一个对象中,最终返回一个`combination`函数(相当于未拆分时传给`createStore`的`reducer`函数) +- 在执行`combination`函数的过程中,它会通过判断前后返回的数据是否相同来决定返回之前的state还是新的state +- 新state会触发订阅者发生对应更新,而旧state可以有效地组织订阅者发生刷新 +