You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
第二个问题,对于多个页面,比如说:一个列表页,一个详情页,一个用户页,要把所有的状态放在一个 store 中么?我倾向于把同一个组的页面的数据放在同一个 State 里面,这里的组大概就相当于 Django 的 App 或者是 Flask 的 Blueprint 的概念。当然,对于小型项目来说,用一个 store 就够了。
总的来说,分以下几步:
确定全局状态,比如用户登录状态,放在 redux 最顶级。
设计每个页面分区可视化树。根据页面布局,确定组件,以及每个组件的状态。
设计 reducers, 同样是一棵树状的组织。
实现 actions, 其中也包括加载数据的 action.
实现展示层,也就是组件们。
页面的布局图和状态树:
使用 normalized data
当我们使用 useSelector 的时候,需要从 state 出发,一层层地获取数据,所以数据的层级最好不要太多,避免出现 state.posts[0].comments[0] 这种冗长的表达式。另一方面,设计不好的状态树会导致好多状态重复存储,或者不好查询。为了解决这个问题,我们可以使用 Redux 官方推荐的解决方案。
从消息队列的角度来理解 Redux
https://ift.tt/Qa8M21f
在 React 中,组件之间是不能互相通信的,数据只能自上而下流动。所以必须把状态放在最高层的 组件中。复杂一点的页面肯定会导致状态越提越高,所以最终还是需要一个单独的状态存储——redux。
Redux 的文档是非常差的。它只写了 What 和 How, 而没有写 Why. 他只说了自己是个状态存储,却 不说为什么需要其他的东西,这就让初学者感到非常地迷惑,实际上它只要提到一下 "Event Bus" 这个词就非常容易理解了,非要上来就讲什么 Action/Store/Reducer/SingleSourceOfTruth 之类的。 根据最高指示:代码是用来读的,不是用来装逼的,俗称 "码读不装", 那么我们这里先来补上 redux 的 Why.
Why Redux?
Redux 本质上来说有很大一部分功能就是一个 Event Bus, 或者 Message Queue, 又或者 Pub/Sub. React 组件之间需要通信,如果让他们之间互相访问,那么就是一个
O(N^2)
的复杂度,而如果让他们通过一个中间组件,也就是 Redux 通信,那么复杂度就大大降低了,更重要的是,代码更清晰了。当然,Redux 不只是负责他们之间的通信,而且是把状态存储了下来。这两篇 文章 写得非常好了。What is Redux?
Redux 里的概念特别多,不过知道了 EventBus, 就都非常好理解了,这里先列出来:
以上就是一个非常基本的 redux 应用啦,如果要有多个 reducer 分开处理不同事件怎么办呢?使用 combineReducers. 需要注意的是,这样的话,每个 reducer 只会获得其中一部分数据了,比如说 userReducer 只会获得 user 部分,itemsReducer 只会处理 items 部分。
React-Redux
在上面的例子中,实际上完全没有涉及到 react 的相关内容,redux 本身就是可以独立存在的一个库。但是,99.99% 的情况下,你还是为了管理 react 的状态采用 redux 的。要在 react 中使用 redux , 需要使用 react-redux 库。
这里需要注意的是,都 2020 年了,我们自然要用 hooks 了,所以网上好多还在讲 connect 的教程可以不用看了。几个常用的钩子:
不使用 hooks 的方式
在没有 hooks 之前,需要使用 react-redux 库提供的 connect/mapState/mapDispatch 几个函数来实现 redux 和 react 组件之间的交互。
设计 redux state tree
我们知道 redux 的 state 是作为一个树存在的,设计这个树的形态是 redux 使用的重中之重了。
第一个问题,是否需要把所有的状态放进 redux 呢?不一定,对于一些组件内部的 UI 状态,比如是否隐藏某个按钮,是可以使用 useState 放在内部的。重点在于:你这个状态其他组件关心吗?一般来说,表单的状态时不需要放进 redux 的。
第二个问题,对于多个页面,比如说:一个列表页,一个详情页,一个用户页,要把所有的状态放在一个 store 中么?我倾向于把同一个组的页面的数据放在同一个 State 里面,这里的组大概就相当于 Django 的 App 或者是 Flask 的 Blueprint 的概念。当然,对于小型项目来说,用一个 store 就够了。
总的来说,分以下几步:
页面的布局图和状态树:
使用 normalized data
当我们使用 useSelector 的时候,需要从 state 出发,一层层地获取数据,所以数据的层级最好不要太多,避免出现
state.posts[0].comments[0]
这种冗长的表达式。另一方面,设计不好的状态树会导致好多状态重复存储,或者不好查询。为了解决这个问题,我们可以使用 Redux 官方推荐的解决方案。不好的方案:
更好的组织方式
而这些数据要放在 entities 中:
何时发送数据请求?
通过 side effects 和 thunk 与服务器通信
从上面的脚本我们可以看出,redux 完全是一个本地的消息处理,然而当我们在本地做出更改的时候,肯定需要在放到服务器啊,这种操作在 redux 中被称作 side effects(副作用), 可以使用 redux-thunk 来实现。
如果是一个组件的内部数据,那么没必要在 redux 中保存状态,也就是使用 useState 就好了。获取数据直接在 useEffect 中 fetch 数据,然后 setState 就可以了。
在 useEffect 中获取数据之后触发 action, 还是通过触发一个 action 来获取数据?这个和私有状态是一样的考虑,如果还有其他的组件会触发这个操作更新数据,那么就使用 action 最好,如果这个数据是组件私有的,那么在 useEffect 中直接获取就好。比如说,对于页面组件,可能直接在组件中使用
useEffect(fn, [])
加载数据就好了,可以直接调用 fetch, 也可以触发一个 action.对于需要获取数据的操作,一般需要三个 Action, 分别是
FETCH_XXX_BEGIN/SUCCESS/FAILURE
. 如果获取数据成功,那么 SUCCESS 的 action 中就会包含数据。另外,还要有一个RESET_XXX_DATA
的 action, 用来清除和重置。一个典型的加载数据的 action:
对应的 Reducer 可以这样:
在 class-based React 中,一般是在 ComponentWill/DidMount 周期中调用加载数据的逻辑,我们现在自然是使用 useEffect 的这个钩子来实现。
redux 项目目录结构
只要分着放 actions 和 reducers 两个目录就可以了,其实没多大要求。我一般是这样放的:
在 index.js 中还是需要
和 React-router 一起使用
当我们和 react-router 一起使用的时候,会遇到一个问题,组件是从 URL 中读取状态,还是从 store 中读取状态呢?这两个状态之间怎么同步呢?redux 的作者 Dan 给出了答案:
不要在 redux 中保存 URL 中的状态,直接从 URL 中读取。可以把 URL 想象成另外一个单独的小的 Store.
遗留问题
如何增加一个新的 Action
参考
如何加载数据
其他的状态管理
via Yifei's Notes
December 27, 2022 at 02:34PM
The text was updated successfully, but these errors were encountered: