redux-batched-actions icon indicating copy to clipboard operation
redux-batched-actions copied to clipboard

batchActions causing a render after each action, rather than after the completion of all three

Open dgaitsgo opened this issue 6 years ago • 8 comments

This is my call with batchActions

dispatch(batchActions([
   this.props.actions.setTags({ tags : activeTags }),
   this.props.actions.setNuggets({ nuggets : activeNuggets}),
   this.props.actions.setSelectedDomain({ selectedDomainId : nextActiveDomain }),
]))

I've put a console.log in each of my reducers. The app crashes before the second one prints out as, since the data is intimately connected, they are mismatched when only one part of the store refreshes. The odd thing is, is this worked as expected before some refactoring. I've tried both calling enableBatching on my rootReducer and without, and same result.

Any ideas?

dgaitsgo avatar Jul 02 '18 21:07 dgaitsgo

Just to be very clear, my intention in using redux-batched-actions is to update separate parts of my store but have only one notification emitted, resulting in one render after all state modifications are complete. Upon closer look to redux source code, dispatch immediately calls all of the listeners once it obtains a new state from the reducer. Nothing a higher order function, like batchActions, could change. Have I misunderstood the purpose or use of this library?

dgaitsgo avatar Jul 03 '18 00:07 dgaitsgo

@dgaitsgo From the sound of it, this library does exactly what you're hoping to achieve. Can you send more context of how you set up the store with your reducer? If you are using enableBatching and batchActions properly, it should be working for you.

tshelburne avatar Jul 03 '18 01:07 tshelburne

Okay,

I've isolated the issue. The code above fails if it is in the callback of a Promise.

api.insert(persistUi).then(res => {
   /*code from above*/
})

but, why?

dgaitsgo avatar Jul 03 '18 02:07 dgaitsgo

I'm sorry I can't make a more generic example to reproduce the error, but this is as simple as I can get.

A promise to wait for :

resolveAfterSecond = x => new Promise (res => setTimeout(() => res(x), 1000) )
test = async (nextActiveDomain) => {
  
  const { allNuggets } = this.props.appData
  const { dispatch } = this.props
  
  // if this await is commented out, function behaves perfectly      
  const foo = await this.resolveAfterSecond('bar')
  
  //these two pieces of data are tightly coupled and must be updated together.
  const activeNuggets = allNuggets.filter(nugget => nugget.domain === nextActiveDomain)
  const activeTags = this.getTagsFromNuggets(activeNuggets)
  
  //With the await statement above, there are renders in between these two actions
  dispatch(batchActions([
    this.props.actions.setNuggets({ nuggets : activeNuggets }),
    this.props.actions.setTags({ tags : activeTags })
  ]))
}

dgaitsgo avatar Jul 03 '18 03:07 dgaitsgo

@dgaitsgo That is truly perplexing. Are your setNuggets or setTags functions asynchronous? I can't imagine why this would split the actions. How are you creating your store?

tshelburne avatar Aug 16 '18 16:08 tshelburne

I reproduced the problem,in my demo project

when I minus, it render one time; but when i add , it render four time;

7tgk8-q6vp6

it occur in Promise and SetTimeout;

WangYuLue avatar Dec 17 '19 07:12 WangYuLue

I have the same feeling. Looking at batchDispatchMiddleware, it seems that it fires store.dispatch multiple times, which causes multiple renders.

Flex301 avatar Jul 06 '20 08:07 Flex301

Can confirm - using batchDispatchMiddleware causes this problem, while using enableBatching doesn't. Unfortunate, because I was reaching for batchDispatchMiddleware as a way to detect / respond to the batched event itself in addition to the children, batched events.

ekilah avatar Jun 09 '22 19:06 ekilah