React + Redux,如何不在每次调度后渲染,而是在多次调度后渲染?

React + Redux, How to render not after each dispatch, but after several?

我正在尝试对商店进行多项更改,但在完成所有更改后才会呈现。我想用 redux-thunk 来做这个。

这是我的动作创作者:

function addProp(name, value) {
    return { type:'ADD_PROP', name, value }
}

function multiGeoChanges(...changes) {
    // my goal here is to make multiple changes to geo, and make sure that react doesnt update the render till the end
    return async function(dispatch, getState) {
        for (let change of changes) {
            dispatch(change);
            await promiseTimeout(2000);
        }
    }
}

我这样发送我的异步动作创建者:

store.dispatch(multiGeoChanges(addProp(1, "val1"), addProp(2, "val2"), addProp(3, "val3")));

然而,这会导致在每个 dispatch 之后做出反应。我是 redux-thunk 的新手,我从未使用过异步中间件,但我认为它可以帮助我。

根据设计,当商店持有的状态更改时应呈现视图。

您可以通过更新一次状态来避免这种情况。

如果您正在使用 promises,您可以使用 Promise.all 等待所有 promises 解决,然后使用计算结果向商店发送新操作。 https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

像这样:

Promise.all([p1, p2, p3, p4, p5]).then(changes => { 
  dispatch(changes)
}, err => {
  // deal with error
});

当然你需要一个处理很多道具的动作,比如addManyProps这应该更新一次状态,导致一次渲染。

实现目标的方法有:

经典方式:

通常: 操作描述了某事发生的事实,但不指定应用程序的状态如何响应而改变。这是减速器的工作。 这也意味着 actions 不是 setter.

因此,您可以描述发生的事情并累积变化,然后调度 one action 类似于:

const multipleAddProp = (changedProps) =>({
   type:'MULTIPLE_ADD_PROP', changedProps
});

然后在 reducer 中对 action 做出反应:

const geo=(state,action)=>{
   ...
   switch (action.type){
   case 'MULTIPLE_ADD_PROP':
     // apply new props
   ...
   }
}

另一种方式 当重新渲染很关键时:

那么你可以考虑限制组件,这些组件可以在状态变化时重新渲染。 例如,您可以使用 shouldComponentUpdate 来检查组件是否 应该渲染还是不渲染。 您也可以使用 reselect,以便不重新渲染连接的组件 计算派生数据后...


非标准方式: redux-batched-action

它的工作原理类似于交易。

在此示例中,订阅者将收到一次通知:

import { batchActions } from 'redux-batched-actions';

const multiGeoChanges=(...arrayOfActions)=> dispatch => {
    dispatch( batchActions(arrayOfActions) );
}

@Kokovin Vladislav 的回答是正确的。要添加一些额外的上下文:

Redux 将在 每次 调度后通知所有订阅者。要减少 re-renders,可以减少发送次数,或者使用几种方法之一进行 "batching" 发送和通知。有关详细信息,请参阅有关更新事件的 Redux 常见问题解答:http://redux.js.org/docs/faq/Performance.html#performance-update-events .

我最近还写了几篇与此主题相关的博文。 Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability discusses the pros and cons of using thunks, and summarizes several ways to handle batching of dispatches. Practical Redux Part 6: Connected Lists, Forms, and Performance 描述了关于 Redux 性能需要注意的几个关键方面。

最后,还有其他几个库可以帮助批量处理商店更改通知。请参阅 Store#Store Change Subscriptions section of my Redux addons catalog for a list of relevant addons. In particular, you might be interested in https://github.com/manaflair/redux-batch ,它将允许您仅使用一个通知事件来分派一系列操作。

redux-batched-actions Batching action creator 和关联的 redux 高阶 reducer,可以为一系列操作启用批处理订阅者通知。

来晚了一点,但我认为这是一个更好的解决方案,它使您可以将 meta.batch 添加到您希望批处理到单个 react 更新中的操作中。作为奖励,这种方法适用于异步操作。

import raf from 'raf'
import { batchedSubscribe } from 'redux-batched-subscribe'

let notify = null
let rafId = null

const shouldBatch = action => action?.meta?.batch

export const batchedSubscribeEnhancer = batchedSubscribe(freshNotify => (notify = freshNotify))

export const batchedSubscribeMiddleware = () => next => action => {
  const resolved = next(action)

  if (notify && rafId === null && !shouldBatch(action)) {
    notify()
  } else if (!rafId) {
    rafId = raf(() => {
      rafId = null
      notify()
    })
  }

  return resolved
}

然后连接到您的商店

mport { applyMiddleware, compose, createStore } from 'redux'
import { batchedSubscribeMiddleware, batchedSubscribeEnhancer } from './batching'

const store = createStore(
  reducer,
  intialState,
  compose(
    batchedSubscribeEnhancer,
    applyMiddleware(batchedSubscribeMiddleware)
  )
)

在 react-redux 7.0.1+ 中,批处理现在是 built-in。 7.0.1 发行说明:

https://github.com/reduxjs/react-redux/releases/tag/v7.0.1

Batched Updates

React has an unstable_batchedUpdates API that it uses to group together multiple updates from the same event loop tick. The React team encouraged us to use this, and we've updated our internal Redux subscription handling to leverage this API. This should also help improve performance, by cutting down on the number of distinct renders caused by a Redux store update.

function myThunk() {

   return (dispatch, getState) => {

       // should only result in one combined re-render, not two

       batch(() => {

           dispatch(increment());

           dispatch(increment());

       })

   }

}