2


UPDATE

The Redux example provided here might be instructive for others: https://github.com/reactjs/redux/tree/master/examples/tree-view


UPDATE

Thank-you for your comments. I am still investigating this, but I am currently exploring an approach similar to what @Chase DeAnda suggested. However, instead of an array, I am using an object with keys equal to the parent component and values equal to what used to be the reducers of the child component. This approach appears to be working, but it's still a WIP. The downside is a deeply nested reducer for the parent component.

This is a pattern discussed in the Redux documentation here: https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape

The link above also discusses ways to address the nesting issue among other better design patterns. I am using this documentation to get the result I want.

Once I am further along, I will update this question with my results & maybe we continue from there. Hopefully the result will be useful for other users in a similar situation. Thanks again!


ORIGINAL QUESTION

I cannot find design advice for the following scenario that is creating race conditions with fetch requests:

  1. There are parent components that can be created dynamically by a user.
  2. Each parent has 5 children that all make fetch requests. These children each have their own reducer to facilitate this.
  3. If I create a new parent in the app, I either need to create new children reducers OR cancel all inflight requests of previous active parent & initiate new requests for currently active parent.

Has anyone encountered a similar scenario? I've read and tried Dan's reply for code splitting here:

https://stackoverflow.com/a/33044701/4240734

AND

How to avoid race conditions when fetching data with Redux?

But the scenarios described above appear different. For one thing, I want to change the active slice reducers based on a non-routing event. In this scenario, I don't have access to the store without violating a design principle. Moreover, even if I do have access to the store, I don't know that replaceReducer is providing the behavior I am wanting it to have.

I have also reviewed the Dan Abramov's Egghead tutorial here:

https://egghead.io/lessons/javascript-redux-avoiding-race-conditions-with-thunks

And indeed, I have already implemented his solutions for avoiding race conditions amongst the children components. The additional level of complexity occurs when switching between the parent components.

I am open to any suggestions. It might also be that my design pattern is off and so don't mind being offered a better solution to architecture.

TLDR;

  1. For a given route on a page, I have an arbitrary number of parent components.
  2. Each parent component has a specific number of children components that all need their own reducers to manage req_sent, req_succeeded, req_failed that are initiated with Fetch (not xhr or other options that have abort options that are well supported).
  3. If a user creates more parent components, (e.g. for a different topic), one of two things need to happen:
    1. More children reducers are created and added to the store for the newly created children components of the newly created parent. OR
    2. All in-flight requests of the previously active parent need to be aborted (again with Fetch requests) and the newly active parent component allows new children requests to go out and populate existing children reducers.

Otherwise, I end up with race conditions from children reducers populating with data for the wrong parent component.

prufrofro
  • 1,477
  • 1
  • 11
  • 12
  • Instead of multiple copies of the same reducer, why not one reducer that can contain an array of each child instance? – Chase DeAnda Mar 09 '18 at 21:13
  • hmmm.. I feel like this is the right direction to go. But let me think on it some more. – prufrofro Mar 09 '18 at 21:44
  • Is there something about your use case that makes redux a better option than using component state? If component state works and each component can handle storing the data and the state associated with fetching its data, might make life simpler. – TLadd Mar 09 '18 at 21:55
  • 1
    Are you sure you actually need to dynamically create new reducers? These child and parent components you mention must have something in common. Which means that their behavior could be modelled in only one reducer for each. It kinda sounds like you shouldn't use the redux state for the request lifecycle if new requests are dynamically spawned by user interaction. I'd go with component state as @TLadd pointed out and only store what really needs to persist in redux. – timotgl Mar 10 '18 at 15:40
  • Thank-you for your comments. I am still investigating this, but I am currently exploring an approach similar to what @Chase DeAnda suggested. However, instead of an array, I am using an object with keys equal to the parent component and values equal to what used to be the reducers of the child component. This approach appears to be working, but it's still a WIP. The downside is a deeply nested reducer for the parent component. But I will explore approaches to mitigate this. Once I am further along, I will update this question with my results & maybe we continue from there. Thanks again! – prufrofro Mar 13 '18 at 21:26

1 Answers1

0

If there is only one active parent component at a time, you could simply attach some sort of ID for the parent component to each individual fetch action.

So if a success action for an outdated parent component is dispatched, for example:

{ type: 'CHILD_FETCH_SOMETHING_SUCCESS', payload: { parentID: 'someParent123', data: { ... }} }

..you would check wether someParent123 is still an active parent component at this point in time. If not, the data can be discarded and the state won't change.

timotgl
  • 2,865
  • 1
  • 9
  • 19
  • Thank-you for your suggestion. I am actually implementing part of this solution. However, it is expensive to make those requests and then dump all of them except for the currently active parent. Instead, I am storing them in the state tree as key=parent/value=child pair. This way, the user can initiate several different parent components and have the results saved in the state upon success. – prufrofro Mar 13 '18 at 23:32