最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React + Redux, How to render not after each dispatch, but after several? - Stack Overflow

programmeradmin10浏览0评论

I am trying to make multiple changes to the store, but not render till all changes are done. I wanted to do this with redux-thunk.

Here is my action creator:

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);
        }
    }
}

I dispatch my async action creator like this:

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

However this is causing react to render after each dispatch. I am new to redux-thunk, I never used async middleware, but I thought it could help me here.

I am trying to make multiple changes to the store, but not render till all changes are done. I wanted to do this with redux-thunk.

Here is my action creator:

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);
        }
    }
}

I dispatch my async action creator like this:

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

However this is causing react to render after each dispatch. I am new to redux-thunk, I never used async middleware, but I thought it could help me here.

Share Improve this question edited Jan 17, 2018 at 11:31 halfer 20.4k19 gold badges108 silver badges201 bronze badges asked Jan 14, 2017 at 2:55 NoitidartNoitidart 37.2k40 gold badges174 silver badges351 bronze badges
Add a comment  | 

6 Answers 6

Reset to default 14

@Kokovin Vladislav's answer is correct. To add some additional context:

Redux will notify all subscribers after every dispatch. To cut down on re-renders, either dispatch fewer times, or use one of several approaches for "batching" dispatches and notifications. For more info, see the Redux FAQ on update events: http://redux.js.org/docs/faq/Performance.html#performance-update-events .

I also recently wrote a couple of blog posts that relate to this topic. 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 describes several key aspects to be aware of regarding Redux performance.

Finally, there's several other libraries that can help with batching up store change notifications. See the 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 , which will allow you to dispatch an array of actions with only a single notification event.

There are ways to achieve the goal:

Classic way:

usually: Actions describe the fact that something happened, but don't specify how the application's state changes in response. This is the job of reducers. That also means that actions are not setters.

Thus, you could describe what has happened and accumulate changes, and dispatch one action something like:

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

And then react on action in reducer:

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

Another way When rerendering is critical :

then you can consider to limit components, which could be rerendered on state change. For example you can use shouldComponentUpdate to check whether component should be rendered or not. Also you could use reselect, in order to not rerender connected components after calculating derived data...


Non standard way: redux-batched-action

It works something like transaction.

In this example, the subscribers would be notified once:

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

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

In react-redux 7.0.1+ batching is now built-in. Release notes of 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());

       })

   }

}

By design when the state, which is held by the store, changes the view should render.

You can avoid this by updating the state once.

If you are using promises you can use Promise.all to wait for all the promises to resolve and then dispatch a new action to the store with the calculated result. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

Something like this:

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

Of course you'll need an action that will deal with many props, something like addManyProps this should update the state once, resulting in one render.

redux-batched-actions Batching action creator and associated higher order reducer for redux that enables batching subscriber notifications for an array of actions.

Coming to this a bit late, but I think this is a much nicer solution, which enables you to add meta.batch to actions you would like to batch together into a single react update. As a bonus this approach works with asynchronous actions.

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
}

Then connect up to your store

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

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

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论