1

I'm trying to build some modular SAP so many teams can work separatelly.

Basically, I want my containers to be independent in terms of container, store, reducers, sagas.

The actual question is (example code):

  1. I render a basic template:

<div> <a onClick={emitLoadUserListAction}>Load user list</a> <UserList/> </div>

At this point, I make use of 1 reducer for UserList to keep the array of users (empty at the beginning).

Let's assume I have a saga, waiting for this data to come as a user list in a json.

Store: { UserList: [] }

  1. Once the saga fetches the data, publishes an action modifiying the current store:

Store: { UserList: [{name:"john",counter:0},{name:"pepe",counter:0}] }

  1. Now my UserList component can list this as we have the mapStateToProps pointing to this part of the store.

this.props.userList.map ( (userData,i) => { return <User data={userData}> } ))

So now everything is working like a charm if User component is just a normal component.

But what if User is actually a container, which is expecting to work on its own, with its own state I didn't connected yet via its own reducer. I don't want his parent to manage it. I want user to be independent as I could pass its location in the store with reselect selector or similar, or I could just pass the index in the array as a prop, so I could be the selector. This way I would have store injected in props, but I won't have reducer.

I'm pretty sure many of you already pass through this but I couldn't find a proper answer.

As you can see the idea is to have a component, which is loading on demand, not in the initial combineReducers, not handled by its parents, just render, and reducer injected to work on its own.

If I could have just a way to load its reducer on demand then, I would not store the data in the UserList but it will be a composition of reducers.

Thanks a lot in advance.

Javier Cobos
  • 1,172
  • 10
  • 22
  • I've built a library for my employer to do exactly this which we are planning on open sourcing soon. It uses [redux-subspace](https://github.com/ioof-holdings/redux-subspace) (another library of mine) to allow components to be independent and isolated from the parent, and supports dynamic reducer loading with solution based heavily on [this answer](http://stackoverflow.com/a/33044701/6902543) by Dan Abramov. – Michael Peyper May 14 '17 at 05:20
  • Hi, do you need to have access to the `store` object from the sub-component to make an injection via reducerReplace? Because in this case, the loaded module doesn't know anything about its parents. – Javier Cobos May 14 '17 at 10:48
  • I've continued as [an answer](http://stackoverflow.com/a/43964655/6902543) so I could go into more detail. – Michael Peyper May 14 '17 at 13:57

2 Answers2

1

I'm continuing on from my comment and the question that followed so I can expand on it without the restrictions of the comments section.


Yes, my library calls replaceReducer on the store to in order to, well, replace the reducer with the new one included. In order to do so, I provide a Higher-Order Component (HOC) which bundles the component with it's associated reducer and performs the replacement when it is mounted.

The interface looks something like this:

export const MyBundledComponent = bundle(MyComponent, myReducer)

The only requirement for it to work is that the component is mounted within a Provider from react-redux. This gives the HOC access to the store on React's context the same way the connect HOC does. This isn't really a very prohibitive restriction though, as most redux apps have a Provider at the top of the tree already.

Hope this helps.

Community
  • 1
  • 1
Michael Peyper
  • 6,814
  • 2
  • 27
  • 44
0

So far I found resources like this:

https://medium.com/@jimmy_shen/inject-reducer-arbitrarily-rather-than-top-level-for-redux-store-to-replace-reducer-fdc1060a6a7

which allow you to inject reducers on demand by replacing the main reducer by using the Redux store API store.replaceReducer(nextReducer)

The problem with this solution is the need to have access to the main store object from the child component that should be encapsulated.

For the moment working not ideal solution that I found is to deliver the encapsulated component with a "multiple components reducers" meaning that the reducer assumes there could be more than one component under the same parent where each one has different ids.

So each action should check the payload ID, in order to get the state from the store object.

This would mean a small change in the hierarchy as the component would not be child but sibling.

Following the previous example, imagine that we list a shallow version of the user list and then you show more data once u click on any user:

`
Store:  {
   UserList: [], // basic info, id plus minimal data
   users: {} --> userReducer // listing each user by key
}
`

This way the user component will expose multiUserReducer instead of logic for just one.

This obviously means the reducer is loaded in advance, even if you never load any user componet.

Javier Cobos
  • 1,172
  • 10
  • 22