Redux: Is it ok and possible to store a react component inside a reducer?

Created on 17 Jan 2016  ·  26Comments  ·  Source: reduxjs/redux

Hi,

I have been trying to store a react component inside a reducer for a project I am working on. I want to know if it is ok to do so and how would I approach it.

I tried to do it, but it loses some functionality inside the reducer.

A react component would look something like this:

{ function:
   { [Function: Connect]
     displayName: 'Connect(Counter)',
     WrappedComponent: { [Function: Counter] propTypes: [Object] },
     contextTypes: { store: [Object] },
     propTypes: { store: [Object] } } }

However, after I store it inside a reducer, it loses its properties and ends looking something like this:

{ function:
   { [Function: Connect] } }

Most helpful comment

What you _might_ want to do is give unique string IDs to some components and store those IDs in state. For example, you might store something like ModalTypes.LOGIN in state.openModalId, and later switch inside your <App> component to choose which modal component to show.

All 26 comments

Could you please explain why do you need to do so?
A store should contain serializable application state, not DOM components which are visual representation of this state.

Never do it, see official advice.
What Shouldn't Go in State?

@simongfxu :+1:

Thank you for the clarification @simongfxu

What you _might_ want to do is give unique string IDs to some components and store those IDs in state. For example, you might store something like ModalTypes.LOGIN in state.openModalId, and later switch inside your <App> component to choose which modal component to show.

@gaearon yes, I took a similar path. I am storing the names (which need to be unique) instead of ids. Then I require the react component where needed. I need to enabe and disable components and reducers on command.

I know this issue is closed, but I wanted to chime-in because I have a similar use-case.

I have a store called editor and a key in that store named activeTool. This key holds a string and it works fine. However, I also have a key named toolbar; in this key, I would like to keep a list of component classes that should be rendered with the active tool.
My action is something like this:

{
  type: 'SELECT_TOOL',
  payload: {
    name: 'toolName',
    tools: [A, List, Of, Tools]
  }
}

So, in the editor reducer, we target this action and do something like the following (with immutable js, but that's probably not important):

return state
  .set('activeTool', payload.name)
  .set('toolBar', payload.tools)

However, dev-tools reports the correct activeTool, but toolBar has been deleted from the editor store. Basically, the same symptoms as OP.

Anyhow, I just wanted to get my use-case out there....
I ended up forgoing the use of the toolBar property in the store and, now, in the component that renders the toolbar, I have an object that maps tool names with an array of components.

Cheers!

@oscar-g You should assign serializable (string or numeric) identifiers to your tools -- this is what you'll be passing via actions and storing in the store, not React components themselves.

I have an object that maps tool names with an array of components.

Yes, this is the only way to go.

Thanks, @sompylasar. I think that is basically what I ended up doing.

  1. action = {type: SELECT_TOOL, name: 'some string'}
  2. Put the name in store.
  3. In component render: return (TOOLMAP[this.props.tool.name])

Where the keys in TOOLMAP map to arrays of components. It is just a constant in the component file.

Is this what you mean?

Yep, this is the way to go.

@simongfxu your link "What Shouldn't Go in State?" URI is gone (redirects to new docs). Could you update link? :)

@Bartuz , @simongfxu : not sure the new docs have an equivalent to that section, actually.

Here's a link to the original page in the Web Archive: React Docs: Interactivity and Dynamic UIs. Also, here's the original content, quoted:

What _Shouldn't_ Go in State?

this.state should only contain the minimal amount of data needed to represent your UI's state. As such, it should not contain:

  • Computed data: Don't worry about precomputing values based on state — it's easier to ensure that your UI is consistent if you do all computation within render(). For example, if you have an array of list items in state and you want to render the count as a string, simply render this.state.listItems.length + ' list items' in your render() method rather than storing it on state.
  • React components: Build them in render() based on underlying props and state.
  • Duplicated data from props: Try to use props as the source of truth where possible. One valid use to store props in state is to be able to know its previous values, because props can change over time.

My 2 cents:

  1. I have a mobile app with a single header component which renders the title of the current page (based on a string in the store) and it may or may not have some specific action buttons for the current page (edit, save/cancel, actions > dropdown with multiple actions, etc). This would be a use-case for passing components via store, e.g. each page "injecting" its own components in the header area.
  2. The approach above would go against the flow for which React and Redux have been built and optimized for. Portals offer an enticing alternative if you want, but then you skip the store. This can be good or bad depending on your monitoring/logging needs. Using Redux, strings or objects with strings can define the buttons, then an 'action' string can trigger behavior by having your component listening to it.

@Dindaleon I got your point and i find it interesting, in spite of what the official React Doc suggests.

I have a similar problem. Not sure if opening an issue is a good idea.

I basically have a plugin system where plugins can register and are allowed to add new components to the UI. I currently add them under a 'components' object within the store. They are added using an action creator and stored there using under the payload key. UI components that allow plugins to be added just check the store if anything has been added. If yes, they pull the component from there and render it.

I've read a lot about adding functions (reducers)/react components to the store. But I couldn't find any good solution to this. I can't really map UI elements to an I'd since I have no way to know each element during compile time. And I somehow need a registration mechanism to make the new UI elements available to the core application.

Did anybody had a similar situation? How did you solve them? I've looked into a lot of open source projects that rent to have a similar problem (atom for example). Those store their components within a ComponentStore (flux).

@SirWindfield : you may want to look at this: http://dylanpaulus.com/reactjs/2017/12/08/global-component-registration/ . It appears to be an extension of the basic approach for handling UI component lookups at runtime, just extended to have a global-ish variable and registration function to add to that table rather than having it be hand-written.

If you need that to be available across the app, you could also write your own <Provider>-type component that uses React's context to make it available to deeply nested components.

@markerikson thanks for the reply!
This looks actually pretty close to the flux idea I had :smile:
It's quite hard to find any resources concerning this topic. It seems like people generally have no interest in writing plugin systems using redux and react.

@SirWindfield : It's definitely a lesser-known topic, yeah.

That said, I've cataloged several "Redux plugin/encapsulation"-type libraries at https://github.com/markerikson/redux-ecosystem-links/blob/master/component-state.md#modularity-and-encapsulation . I haven't yet had time to try any of them out myself. Someday I'd like to be able to experiment with them and get an idea of what they're capable of, but I just have too much else to do. Still, you might want to look through them and see if anything matches your use case.

@markerikson thanks! This list is awesome.

What if I want to store in state a File object, for upload tracking purpose. Should this be ok?

Actually no, I can store only serializable objects.

You could store the path of the file though

@albanx potentially yes, you could encode the file (base64) and store it whenever you desire. The question is: do you really need to store a file?

@SirWindfield I meant a HTML5 File, Blob (when user selects a file in the browser to upload).
@soulclown I really do not need the content to the file, but just a reference to the File element. I am creating an uploader and I want to keep track of the uploaded files and progress in the state. I will end up creating an ID for the files and store that in the state.

Interesting case, Alban. Keep us updated what's your solutions to this

[image: Mailtrack]
https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5&
Sender
notified by
Mailtrack
https://mailtrack.io?utm_source=gmail&utm_medium=signature&utm_campaign=signaturevirality5&
08/01/18,
3:12:04 PM

2018-08-01 10:28 GMT+02:00 Alban notifications@github.com:

@SirWindfield https://github.com/SirWindfield I meant a HTML5 File,
Blob (when user selects a file in the browser to upload).
@soulclown https://github.com/soulclown I really do not need the
content to the file, but just a reference to the File element. I am
creating an uploader and I want to keep track of the uploaded files and
progress in the state. I will end up creating an ID for the files and store
that in the state.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/reduxjs/redux/issues/1248#issuecomment-409494273, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AEL3RHZQT-Kp-MLFoUZryI0hrals0MFaks5uMWa8gaJpZM4HGj-F
.

FWIW, as @markerikson sort of hinted at regarding plugin frameworks, I've been storing React components, reducers, and middleware in redux state for years using my react-features and react-redux-features packages, with SSR.

It works because I don't serialize the features part of the state tree that contains the React components. I just serialize which feature ids were loaded during SSR (these are the unique global ids @gaearon
suggested). But where I depart from consensus is, when bootstrapping clientside I rebuild the non-serializable part from scratch, loading the content for the applicable feature ids.

The one downside is it wouldn't work with time traveling. But in all my debugging I've never really found myself craving a time traveling debugger...redux logs are good enough for me.

I suppose I could move the non-serializable state into a separate store of sorts with its own context provider. Maybe. It might be a different PITA though?

I arrived at this design because having separate global component and reducer lookup tables would be a lot more cumbersome to coordinate with async chunk loading.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

timdorr picture timdorr  ·  3Comments

caojinli picture caojinli  ·  3Comments

captbaritone picture captbaritone  ·  3Comments

ms88privat picture ms88privat  ·  3Comments

parallelthought picture parallelthought  ·  3Comments