react 数据管理发展

308 阅读4分钟

redux

普通搭配使用redux, 在组件內获取redux数据, 需要用redux的方法, 比如用store的一些方法, getstate获取数据, dispatch触发方法等, 但是这样做弊端

1. react和redux耦合度太高, 在组件中到处看到redux api

2. 将redux的store数据先当成根组件的属性值, 然后通过props层层传递到其他的组件, 进而其他组件拿到对应需要的store, 这样的数据流转过于心累

react-redux

这个第三方库,  提供了connect方法和provide容器组件, connect会把react和redux连接起来, provider容器组件相关知识 

www.cnblogs.com/itgezhu/p/1…

blog.csdn.net/q1056843325…

useSelector

hooks出现后,react-redux提供了useSelector等hooks,作为可以替代connect()高阶组件方式的一种选择。这些hooks可以让我们订阅Redux store和dispatch actions,而不用connect()来包裹我们的components。下面是使用hooks实现的方式:

和connect()方式相比,hooks方式有什么不同呢?

1.更少的样板代码。useSelector()使我们不再需要区分UI组件和container组件。

2.useSelector用于从Redux存储的state中提取值并订阅该state。这基本上类似于在hooks中实现的mapStateToProps函数,但有一些小的差异:

  • 不再提供ownProps API,并且应该使用useCallback或useMemo来通过自定义逻辑获取它们
  • useSelector默认使用 === 严格相等来检查,而不是浅比较
  • 需要考虑使用reselect等记忆选择器来提高性能

3.connect()只有在props改变时才会重新渲染,hooks方式也可以通过使用React.memo来实现同样的效果。

function CounterUseSelector({ allowValueChange }) {

const count = useSelector(selectCount)

const dispatch = useDispatch()

// ...

}

export default React.memo(CounterUseSelector)

4.可能会出现'stale props' 和 'zombie child' 的问题

在version 4 之前,一个使用了mapState的list item,如果刚刚被移除,可能会引起错误。在version 7,React Redux使用改写部分内部的React Context来解决这个问题,但对于hooks来说,没有重新渲染context provider的方式,所以'stale props' 和 'zombie child' 的问题可能会重新出现。

具体来说,以下情况会引起'stale props':

  • 选择器依赖于组件的props来提取数据
  • 某个action会引起父组件的重新渲染并把新的props传递给该组件
  • 但是该组件的选择器函数会在该组件使用新的props重新渲染前执行

由于依赖于props和store state,这种情况可能会拿到错误的数据,甚至会抛出错误。

以下情况会引起'zombie child':

  • 第一次挂载多个嵌套的connected component,导致子组件先于父组件订阅store
  • dispatch了一个从store中删除数据的action,例如 todo item
  • 父组件会停止渲染子组件
  • 然而,因为子组件是先订阅的,subscription会在父组件停止渲染前执行。当基于props读取store中的数据时,这个数据已经不存在了,可能会抛出错误

我们可以采取以下方法来避免这些错误:

  • 在选择器中,不要依赖于props来提取数据
  • 如果需要依赖props来提取数据,要使代码更健壮。比如不要直接使用 state.todos[props.id].name ,首先先读取 state.todos[props.id] ,确定存在后再读取 todo.name
  • 因为 connect 增加了对context provider 的必要的订阅并且在组件重新渲染时延迟计算子组件的订阅,所以把使用useSelector的connected components放在上层会避免这些错误

useSelector 作用

调用此 Hook API 时会在 store 上注册监听器。 当 Store::state 变化时,组件会 checkForUpdates,利用 equalityFn 判断是否进行更新。

缺点

没有对 selector 函数做 memorize 优化

解决方案

使用 reselect 对 selector 做 memorize 处理

reselect作用

对 selector 函数(等效于 mapStateToProps 函数)做 memorize 优化,如果 selector 的入参没有发生变化,则返回上一次执行的缓存。

reselect源码其实很短,主要是实现了一个记忆函数

useSelector源码细节

在使用hooks之前,需要将store注入到组件中

const store = createStore(rootReducer)

ReactDOM.render(

<Provider store={store}>

<App />

</Provider>,

document.getElementById(``'root'``)

)

注意:这个Provider是react-redux封装后的Provider,不是Context.Provider,Context.Provider的属性是value。

封装的Provider为:

通过store.subscribe(listener),将checkForUpdate作为listener,当store改变并且选取的state上次不同时,会调用forceRender()重新渲染。

总结

1.useSelector让我们使用react-redux更加方便,可以用来代替connect()的方式。

2.使用方式为 const count = useSelector(selectCount);

3.useSelector本身没有记忆功能,可以用reselect来增加记忆功能,避免重复进行复杂的计算

4.useSelector可能会出现一些预料之外的问题,写代码时应避免出现这些问题