New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it ok and possible to store a react component inside a reducer? #1248

Closed
Dindaleon opened this Issue Jan 17, 2016 · 25 comments

Comments

@Dindaleon

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] } }
@sompylasar

This comment has been minimized.

Show comment
Hide comment
@sompylasar

sompylasar Jan 17, 2016

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.

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.

@simongfxu

This comment has been minimized.

Show comment
Hide comment
@simongfxu

simongfxu Jan 18, 2016

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

simongfxu commented Jan 18, 2016

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

@sompylasar

This comment has been minimized.

Show comment
Hide comment
@Dindaleon

This comment has been minimized.

Show comment
Hide comment
@Dindaleon

Dindaleon Jan 18, 2016

Thank you for the clarification @simongfxu

Thank you for the clarification @simongfxu

@Dindaleon Dindaleon closed this Jan 18, 2016

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jan 19, 2016

Collaborator

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.

Collaborator

gaearon commented Jan 19, 2016

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.

@Dindaleon

This comment has been minimized.

Show comment
Hide comment
@Dindaleon

Dindaleon Jan 19, 2016

@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.

@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.

@oscar-g

This comment has been minimized.

Show comment
Hide comment
@oscar-g

oscar-g Feb 19, 2016

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 commented Feb 19, 2016

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!

@sompylasar

This comment has been minimized.

Show comment
Hide comment
@sompylasar

sompylasar Feb 19, 2016

@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.

@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.

@oscar-g

This comment has been minimized.

Show comment
Hide comment
@oscar-g

oscar-g Feb 19, 2016

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?

oscar-g commented Feb 19, 2016

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?

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Feb 19, 2016

Collaborator

Yep, this is the way to go.

Collaborator

gaearon commented Feb 19, 2016

Yep, this is the way to go.

@Bartuz

This comment has been minimized.

Show comment
Hide comment
@Bartuz

Bartuz Nov 17, 2016

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

Bartuz commented Nov 17, 2016

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

@markerikson

This comment has been minimized.

Show comment
Hide comment
@markerikson

markerikson Nov 17, 2016

Contributor

@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.
Contributor

markerikson commented Nov 17, 2016

@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.
@steodor

This comment has been minimized.

Show comment
Hide comment
@steodor

steodor Jul 6, 2017

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.

steodor commented Jul 6, 2017

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.
@soulclown

This comment has been minimized.

Show comment
Hide comment
@soulclown

soulclown Jul 28, 2017

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

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

@SirWindfield

This comment has been minimized.

Show comment
Hide comment
@SirWindfield

SirWindfield Dec 10, 2017

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 commented Dec 10, 2017

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).

@markerikson

This comment has been minimized.

Show comment
Hide comment
@markerikson

markerikson Dec 10, 2017

Contributor

@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.

Contributor

markerikson commented Dec 10, 2017

@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.

@SirWindfield

This comment has been minimized.

Show comment
Hide comment
@SirWindfield

SirWindfield Dec 10, 2017

@markerikson thanks for the reply!
This looks actually pretty close to the flux idea I had 😄
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.

@markerikson thanks for the reply!
This looks actually pretty close to the flux idea I had 😄
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.

@markerikson

This comment has been minimized.

Show comment
Hide comment
@markerikson

markerikson Dec 10, 2017

Contributor

@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.

Contributor

markerikson commented Dec 10, 2017

@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.

@SirWindfield

This comment has been minimized.

Show comment
Hide comment
@SirWindfield

SirWindfield Dec 11, 2017

@markerikson thanks! This list is awesome.

@markerikson thanks! This list is awesome.

@albanx

This comment has been minimized.

Show comment
Hide comment
@albanx

albanx Aug 1, 2018

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

albanx commented Aug 1, 2018

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

@albanx

This comment has been minimized.

Show comment
Hide comment
@albanx

albanx Aug 1, 2018

Actually no, I can store only serializable objects.

albanx commented Aug 1, 2018

Actually no, I can store only serializable objects.

@SirWindfield

This comment has been minimized.

Show comment
Hide comment
@SirWindfield

SirWindfield Aug 1, 2018

You could store the path of the file though

You could store the path of the file though

@soulclown

This comment has been minimized.

Show comment
Hide comment
@soulclown

soulclown Aug 1, 2018

@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?

@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?

@albanx

This comment has been minimized.

Show comment
Hide comment
@albanx

albanx Aug 1, 2018

@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.

albanx commented Aug 1, 2018

@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.

@Bartuz

This comment has been minimized.

Show comment
Hide comment
@Bartuz

Bartuz Aug 1, 2018

Bartuz commented Aug 1, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment