Hacker News new | past | comments | ask | show | jobs | submit login

This type of data flow where components need to plug into multiple instances of the same type of state is an area where Redux doesn't help you, and solutions tend to be ad-hoc, using things like the "special keys" you mention.

A challenge I had recently was a location form field, where the user is offered autocompletions as they type. The autocompletion state is specific to the component; every field needs its own unique state. So to represent this with Redux, the component has to generate a "state ID" (basically just "new Object()", as I recall) that serves a unique identifier that it can request lookups under. The autocomplete() action then has to take a state ID as a parameter, and associate all lookup state with that ID. Internally, I implemented this as a layer on top of the autocomplete engine, so that the underlying layer didn't know about the ID. Even so, it wasn't beautiful. And of course it was susceptible to leaks -- it was necessary to "unregister" the state ID on unmount. It was unpleasant enough that I considered not using Redux, but I've done that before, and I really wanted the whole state transition machinery.

Has anyone created a solution for this, I wonder? It's possible that a new Redux store per field would have solved everything. But can you connect a component to multiple stores? Some components need to have instance data and access the global store (e.g. current logged in user).




Yes, there's been quite a few libs already created to try to implement per-component-state tracking in Redux:

https://github.com/markerikson/redux-ecosystem-links/blob/ma...

That said, did the component really _need_ the autocomplete state to be stored in Redux in the first place?


Amen!

Creating solutions to problem we invent.

There needs to be a line between UI state and application state.

I've had similar issues with MVVM in WPF. Do you really need a observable property for "UserClickedThisSoMakeButtonGreenAndShiftThisPanelOverALittleBit" in your view model? I'm exaggerating of course, but there is a line. There is nothing wrong with UI specific code in your component.cs code-behind, same story for react.


Thanks for the link. Well, all state transition mechanics benefit from the strict, pure serializability that Redux offers.

In this case, it was a component that not only autocompletes a location, but also offered to geolocate the user (via the browser's geolocation API) and supported a few other features that meant it was several competing concerns wrapped into a single UI. Those can get hairy if you don't do a Redux-type state machine.


I’m curious what the shape of your redux state tree and component tree looked like. It sounds like you had a collection of n components, each with its own autocomplete component. Was there not an obvious parallel in the redux state tree for each of these n components?

Also, as a side note, you may want to check out recompose’s withReducer HOC, which is effectively a way to use the Redux action+reducer flow to manage the state of a single component (without the separate store, the context stuff, etc. that Redux provides):

https://github.com/acdlite/recompose/blob/master/docs/API.md...

In case you’re interested, ReasonReact also provides a reducer component as the only way to have a stateful component. The pain of “boilerplate” basically goes away in a typed language:

https://github.com/reasonml/reason-react/blob/master/docs/st...


You could use Symbols or create a Symbol type (just an object with .toString method) to make the state IDs more manageable. It does amount to writing a memory manager similar to object pooling in a game engine. It's not that bad if the codebase has one convention for how to manage it, but if every case is adhoc it gets nasty quickly.

In general the solution would be some kind of class with .acquire and .release methods to create/recycle objects as needed. It comes up often enough that I'm a little surprised there isn't at least one or more popular libraries for it.


I think the fact that you’re storing UI-specific data (even if it is just IDs) in Redux, is a sign that it is the wrong tool.


Why?

In my own app, I store a variety of data in the Redux store: data fetched from the server, UI state like modals/context menus/tabs, app control state like "current selected item", etc. Certainly not all UI state _needs_ to go into Redux, but there's nothing wrong with storing UI state in Redux. Totally depends on where and how it's needed.


I have no problem with storing things like modal state, currently selected items, etc. in Redux, but I’m having trouble imagining a scenario where you would need to generate a random ID to associate a piece of state with an arbitrary component. Generally the Redux state tree would store the piece of UI state inside the “model” it is associated with.

As a super simple example, if you had a list of continents, and each continent could be expanded (to list every country inside of it) or collapsed, your Redux state might look like:

    {
        'continents': [
            'Asia': {
                'collapsed': false,
                'countries': [
                    'Mongolia',
                    'Japan',
                    // ...
                ],
            },
            'North American': {
                'collapsed': true,
                'countries': [
                    'Canada',
                    'Mexico',
                    // ...
                ],
            },
        ],
    }
The 'collapsed' key is purely UI state for your collapsible component (i.e. you probably wouldn't sync it to a server), but it is keyed naturally in the Redux state along with the normal Redux data it's related to.

In what case would you need to generate a random ID per component simply to "remember" where the data for each component is located in Redux?


For example if you have multiple lists of continents and each one needs its own collapsed state. Maybe the user can even dynamically add/remove such continent lists in the UI. IMHO in most of these cases it's best to keep the continents data inside of redux and have the collapsed state being managed by the "ContinentsList" component.


Still, wouldn’t the list of continent-lists be stored as an array in Redux?

Yes, generally something like expanding an accordion component can just sit in the local component state, as long as the collapsed state doesn’t need to affect or be affected by other app state.


I wouldn't store it as array in Redux because that would be too limiting (e.g. let's say I want a continent-list which is decoupled from the other continent-lists). If I would be forced to use redux in this case then I would create some random id for each ComponentList where I would store the UI state (but keep the continents data separated). Something like:

  {
    '<randomId>':
    {
        Asia:
        {
            collapsed: true
        }
    }
  }
IMHO this really makes things unnecessary complicated. It also makes it hard to re-use the ContinentsList component (maybe you want to use the same component in some other project which uses something else than redux?). I always try to think of the API of my react components first and how they later on can be easily reused. After I figured out the API of the react component then I might connect it to some store.

Probably the ContinentsList component would somehow look like this:

  <ContinentsList
    continents={<continents data>}
    renderItem={({ collapse, isCollapsed, continent }) => {
        return <ContinentItem onCollapseClick={collapse} isCollapsed={isCollapsed} continent={continent} />
    }}
  />


I’m still having trouble imagining an application where you would need ContinentLists in different spots without some well-defined arrangement of them in the Redux state where each can be identified by something more concrete than a random ID.


Yeah, I agree that this is an edge case (nothing that I run into on a daily base) and the ContinentLists example might not be the best in this context. Something like a Dashboard with dynamic widgets would probably be a more realistic case.


I've always thought of it like this -

If the state is likely to be a common concern, or referenced in another component, them redux it up. If the rest of the app doesn't know/care then scope that state.

If you're using Provider or subscribe() you're going to end up hammering lifecycle methods unnecessarily in relatively trivial UI changes. Just seems unnecessary.


Yep, that's basically what the Redux FAQ entry on "can I use setState?" says: https://redux.js.org/faq/organizing-state#do-i-have-to-put-a... . I'll paste the key parts of the answer:

> Using local component state is fine. As a developer, it is your job to determine what kinds of state make up your application, and where each piece of state should live. Find a balance that works for you, and go with it.

> Some common rules of thumb for determining what kind of data should be put into Redux:

> - Do other parts of the application care about this data?

> - Do you need to be able to create further derived data based on this original data?

> - Is the same data being used to drive multiple components?

> - Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?

> - Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: