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
importReactfrom'react'import{useSelector}from'react-redux'import{createSelector}from'reselect'constselectNumOfDoneTodos=createSelector(state=>state.todos,todos=>todos.filter(todo=>todo.isDone).length)exportconstDoneTodosCounter=()=>{constNumOfDoneTodos=useSelector(selectNumOfDoneTodos)return<div>{NumOfDoneTodos}</div>}exportconstApp=()=>{return(<><span>Number of done todos:</span><DoneTodosCounter/></>)}
importReact,{useMemo}from'react'import{useSelector}from'react-redux'import{createSelector}from'reselect'constmakeNumOfTodosWithIsDoneSelector=()=>createSelector(state=>state.todos,(_,isDone)=>isDone,(todos,isDone)=>todos.filter(todo=>todo.isDone===isDone).length)exportconstTodoCounterForIsDoneValue=({ isDone })=>{constselectNumOfTodosWithIsDone=useMemo(makeNumOfTodosWithIsDoneSelector,[])constnumOfTodosWithIsDoneValue=useSelector(state=>selectNumOfTodosWithIsDoneValue(state,isDone))return<div>{numOfTodosWithIsDoneValue}</div>}exportconstApp=()=>{return(<><span>Number of done todos:</span><TodoCounterForIsDoneValueisDone={true}/><span>Number of unfinished todos:</span><TodoCounterForIsDoneValueisDone={false}/></>)}
useDispatch()
constdispatch=useDispatch()
该 hook 返回 Redux store 的 dispatch 函数引用。你可以按照需要来使用它派发 actions。
例子
importReactfrom'react'import{useDispatch}from'react-redux'exportconstCounterComponent=({ value })=>{constdispatch=useDispatch()return(<div><span>{value}</span><buttononClick={()=>dispatch({type: 'increment-counter'})}>
Increment counter
</button></div>)}
importReact,{useCallback}from'react'import{useDispatch}from'react-redux'exportconstCounterComponent=({ value })=>{constdispatch=useDispatch()constincrementCounter=useCallback(()=>dispatch({type: 'increment-counter'}),[dispatch])return(<div><span>{value}</span><MyIncrementButtononIncrement={incrementCounter}/></div>)}exportconstMyIncrementButton=React.memo(({ onIncrement })=>(<buttononClick={onIncrement}>Increment counter</button>))
useStore()
conststore=useStore()
该 hook 返回一个 与 从 <Provider> 组件传递的 Redux store 相同的引用。
该 hook 不应该被频繁使用。useSelector() 更应该成为你的首选。但是,该 hook 在某些需要访问 store 的场景下也是有用的,比如说替换 reducers。
importReactfrom'react'import{useStore}from'react-redux'exportconstCounterComponent=({ value })=>{conststore=useStore()// EXAMPLE ONLY! Do not do this in a real app.// The component will not automatically update if the store state changesreturn<div>{store.getState()}</div>}
constCounterComponent=({ name })=>{constcounter=useSelector(state=>state.counter)return(<div>{name}: {counter}</div>)}exportconstMemoizedCounterComponent=React.memo(CounterComponent)
Hooks 秘籍
聚焦于更小规模的 API 实现我们已经在 alpha 版本中缩减了 hooks API。但是,你可能仍然希望在应用中使用这些方法,可以直接复制粘贴下面的例子到你的代码中。
【译】React Redux API Hooks
React Redux 原文
React 新 "hooks" APIs 为函数组件提供使用本地组件状态以及执行副作用操作等特性。
现在 React Redux 提供了一系列 hook APIs 来替代已经存在的
connect()
高阶组件。 这些 APIs 允许你订阅 Redux store 和派发 actions,使得包裹组件在connect()
里不再是必须。hooks 在
v7.1.0
中首次被添加。在 React Redux App 中使用 Hooks
和
connect()
一样,你需要将整个应用包裹在<Provider>
里以确保在组件树中 store 是可用的。从现在开始,你就可以在函数组件里导入和使用列出的 React Redux hooks APIs 了。
useSelector()
基本使用
使用一个 selector 函数,允许 你从 Redux store state 中提取数据
selector 近似于 和 mapStateToProps argument to connect 等价。selector 只接受 Redux store state 作为它仅有的参数。当函数组件渲染时 selector 会被执行。
useSelector()
将订阅 Redux store,派发 action 时,selector 执行。然而,
useSelector()
和mapState
函数参数中的 selectors 还是有很多不同之处。useSelector()
hook 的返回值。useSelector()
会浅比较(===)上一个 selector 的返回值和当前值。如果不相等,组件被重新渲染,否则,不会被重新渲染。ownProps
参数。但是,props 可以通过闭包(更多细节看下面 通过闭包使用 props 提取数据 例子)来使用或者通过柯里化 selectoruseSelector()
默认使用严格===
做引用比较,而不是浅比较(更多细节看后面 相等比较和更新 部分)你可能在单一函数组件里多次调用
useSelector()
,每一次调用useSelector()
都创建了单独的 Redux store 订阅。由于在React Redux v7
中,使用了 React 的批量更新特性,在同一个组件中派发一个 action 可能导致多个useSelector()
s 返回新值,但是只会导致一次重新渲染。相等比较和更新
当函数组件渲染时,selector 函数将会被调用,它的结果作为
useSelector()
hook 的结果返回。(如果 selector 已经运行并且没有改变将会返回一个缓存值。)但是,当向 Redux store 中派发 action 时,如果 selector 结果不同于上一个值,
useSelector()
强制重新渲染。和v7.1.0-alpha.5
一样,默认比较为严格===
引用比较。connect()
则不一样,mapState 调用的结果使用浅比较来决定是否有必要重新渲染。还有一些如何使用useSelector()
的贴士。在
mapState
中,所有的字段都在一个聚合对象中,不介意是否返回的对象是否是新引用 -connect()
仅仅比较每一个字段。在useSelector()
中,默认情况下每一次返回一个新对象总会强制重新渲染。如果想要从 store 中 提取多值,你可以:useSelector()
,每一次调用都返回一个单一字段Reselect
或者其他类似库来创建 memoized selector 返回一个聚合的对象,仅在值改变的情况下返回新对象shallowEqual
函数作为useSelector()
的参数,如下:可选的比较函数也可以使用
Lodash
的 _.isEqual() 或者Immutable.js
的比较特性。例子
通过闭包使用 props 提取数据
使用 memoizing selectors
当在
useSelector
中使用上面示例代码中的行内 selector
时,在组件渲染时,selector 的新实例被创建,在这种情况下,selector 不维护任何状态。然而,memoizing selectors(例如 通过 reselect 创建的 createSelector)有内部 state,因此,使用时要特别注意。从下面你可以找到 memoizing selectors 的特殊使用场景。当 selector 仅依赖于 state,请确保 selector 定义在 组件外部以便每一次渲染 selector 实例都是相同的。
如果 selector 依赖于组件的 props,selector 定义在 组件外部,仅被用于单一组件的单例中:
然而,如果依赖于组件 props 的 selector 被用于多个组件实例中,你必须确保每一个组件实例都有它自身的的 selector 实例(看 这里 获取更多详尽这样做的原因)
useDispatch()
该 hook 返回 Redux store 的 dispatch 函数引用。你可以按照需要来使用它派发 actions。
例子
当使用 dispatch 传递 callback 给子组件时,推荐使用 useCallback memoize callback,否则,子组件可能会有不必要的渲染。
useStore()
该 hook 返回一个 与 从
<Provider>
组件传递的 Redux store 相同的引用。该 hook 不应该被频繁使用。
useSelector()
更应该成为你的首选。但是,该 hook 在某些需要访问 store 的场景下也是有用的,比如说替换 reducers。使用注意事项
过期 Props 和 “僵尸子组件”
React Redux 实现中最棘手的问题是如果你的
mapStateToProps
函数以(state, ownProps)
方式定义,保证每次都使用 “最新” 的 props 来调用。在v4
中,在某些边界情况下频繁报出 bug,比如说:列表项的数据被删除,mapState
函数抛出异常。在
v5
中,React Redux 尝试保证 ownProps 的一致性。在v7
中,connect()
内部使用自定义Subscription
类来实现,不过这种实现导致层级嵌套。这样确保在组件树中层级较深的connected
组件只会在最近的connected
祖先组件更新时,接受 store 更新通知。然而,这依赖于每一个connect()
实例覆盖内部React context
,提供唯一Subscription
实例来组成嵌套和使用新的 context 值渲染<ReactReduxContext.Provider>
。使用 hooks,没有途径来渲染
<ReactReduxContext.Provider>
,这意味着在 subscriptions 中没有层级嵌套。基于此使用 hooks 来替代connect()
可能还会导致在应用中出现 “过期 props” 和 “僵尸子组件” 问题。明确来言,“过期 props” 发生的场景有:
这依赖于什么 props 被使用和 当前的 store state 是什么,可能导致从 selector 中返回错误的数据甚至抛出错误
“僵尸” 子组件发生的场景有:
connected
组件挂载,导致子组件订阅 store 早于它的父组件。useSelector()
尝试通过捕获所有错误来解决 store 更新(不是在渲染期间的执行)selector 执行时的报错。在错误发生后,组件被强制渲染,selector 重新执行。只要 selector 是纯函数以及不依赖于 selector 的报错,以上策略会生效。如果你更喜欢自己解决这个问题,下面是一些避免这些问题可行的使用
useSelector()
的解决方案:state.todos[props.id].name - read state.todos[props.id]
,在尝试读取todo.name
之前先要验证一它是否存在connect
添加了必要的Subscription
给<ReactReduxContext.Provider>
,并且直到connected
组件重新渲染时才会延迟计算子组件的 subscriptions,在组件树中使用useSelector
的组件上面添加一个connected
组件会避免一些问题,只要connected
组件和 hooks 组件一样,因为同一个 store 更新重新渲染。优化
正如上面提到的,当 派发 action 后,selector 函数运行时,
useSelector()
默认做选择数据的引用相等比较,如果选择的数据改变会导致组件重新渲染。然而,不像connect()
,useSelector()
不会阻止父组件重新渲染导致的组件重新渲染,尽管组件的 props 没有发生变化。进一步做性能优化是必要的,你可以考虑把你的函数组件包裹在
React.memo()
里:Hooks 秘籍
聚焦于更小规模的 API 实现我们已经在 alpha 版本中缩减了 hooks API。但是,你可能仍然希望在应用中使用这些方法,可以直接复制粘贴下面的例子到你的代码中。
秘籍之一:useActions()
该 hook 在我们原本的 alpha 版本中,但是在 Dan Abramov's 建议 下于
v7.1.0-alpha.4
中被移除。该建议基于在 hooks 使用场景下 "binding action creators" 不如以前有用,可能会引起很多理解负担的和语法复杂。你可能更应该在你的组件中调用
useDispatch
hooks 来获得 dispatch 的引用和需要时在 callbacks 和 effects 中手动调用dispatch(someActionCreator())
。你可以在你的代码中使用 Redux bindActionCreators 函数或者像这样const boundAddTodo = (text) => dispatch(addTodo(text))
来 “手动” 绑定。然而,如果你仍然想要使用该 hook,下面是一个拷贝版本用来支持传递函数、数组和对象类型的 action creators。
秘籍之二:useShallowEqualSelector()
The text was updated successfully, but these errors were encountered: