Skip to content
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

Transient state #14

Open
anthonyshort opened this issue Aug 13, 2015 · 5 comments
Open

Transient state #14

anthonyshort opened this issue Aug 13, 2015 · 5 comments

Comments

@anthonyshort
Copy link

I've been thinking a bit lately about how an architecture like this would solve the problem of local, transient UI state in a tree that can be quite deep.

For example, let's say we want to have some state when an icon is hovered over so we can change the color of the svg element within. We could use an action to change the state at the top-level, but how do we go about storing this type of state? It's state that is relevant only to icon itself, e.g activeIcon: true, not the entire app. The same could be said for the open state of select menus, or whether an accordion-style UI element is open.

If we store this state along with our app state, we have a couple of new problems:

  • We need to pass this transient state down through functions and components that don't care about it, which makes the functionality of a much deeper UI element dependant on a higher level component (or multiple) passing the state down through the tree. This breaks the "only care about one level down" approach.
  • We need to identify the state with this single part of the UI so that when we render it gets given the correct state. How do we do this? Using it's path? A unique ID? In the accordion example, would we have app state with a key of accordion and store the state there? What about the current hovered icon example?
  • If state updates are kept next to the component, as in this article, then how do we manage updating the same state across many parts of the UI? For example, if I have a project, I might be able to edit that in multiple ways. If we're just throwing actions upwards what component should own the ability to update the project state? My guess is the first common ancestor. This higher-level component could be responsible for updating the state of many different parts of the UI so that the state can be passed back down correctly.

After building and working with Deku for a year or so most of my thoughts are similar to the work you're doing here. If we can keep all state out of the virtual DOM, use bubbling actions to manipulate state, and hold all state outside of the tree, then everything becomes much simpler. I'm struggling to think about:

  • Where local, transient state should live, or if it should exist at all
  • How we pass state down without needing to pass it through components that don't care about it. Maybe this is just the way apps need to be built. There needs to be a series of components that are coupled to the app state structure.

I'm really curious to know what people who are approaching UI in a similar way to the Elm architecture are thinking regarding these problems. Hopefully I'm missing some really obvious, but I haven't yet tried building anything of real complexity using an approach like this.

@paldepind
Copy link
Owner

Hello @anthonyshort. It's nice to have you here. Deku is really cool!

You seem to be making a separation between "app state" and "transient state". I don't understand why you see these things so differently (it might be because other frameworks separates them). You probably know it, but the Elm architecture uses the "big state approach", similar to Om. All state is stored in a single big data structure. That includes business state (things you might want to persist), temporary view state and everything else. Conceptually these things are of course very different. But I don't see why we can't handle them equally.

We need to pass this transient state down through functions and components that don't care about it, which makes the functionality of a much deeper UI element dependant on a higher level component (or multiple) passing the state down through the tree. This breaks the "only care about one level down" approach.

Yes. You are right that we have to pass state down through several components. But in a way they will care about it. They won't care about the content but they will care about passing it one level further down the chain. That makes the flow of data explicit and is inherent to the architecture. I don't see how that breaks the "only care about one level down" principle. Consider something like this:

Component1 -> Component2 -> Component3 -> Component4

Each component only know what it receives and what it has to pass on. But Component2 doesn't know if there is 1 or 5 components above it. It also has no idea that there is two components beneath it. This guarantees infinite nestability and loose coupling.

If state updates are kept next to the component, as in this article, then how do we manage updating the same state across many parts of the UI? For example, if I have a project, I might be able to edit that in multiple ways. If we're just throwing actions upwards what component should own the ability to update the project state? My guess is the first common ancestor. This higher-level component could be responsible for updating the state of many different parts of the UI so that the state can be passed back down correctly.

Yes, my answer would be exactly the same. If several components depends on the same data the data will have to originate in a common ancestor since data can only flow down through the functions.

Have you seen the nesting example? It might answer some of your questions.

I think your questions are very interesting and I'm not sure if you will find my answers acceptable. Basically I don't see transient state as something that different from everything else. If you can think of a simple example that would demonstrate your concern I'd like to take a shot at implementing it.

@ericgj
Copy link
Contributor

ericgj commented Aug 14, 2015

@anthonyshort great practical questions. These kinds of things I have been struggling with as well, looking at Mercury, Om, Reagent, Elm, and various Flux alternatives.

To me, one nice part about the Elm approach is although there is a single state atom, you mostly don't need to think about it that way. As @paldepind said, you just have to think about what a child component needs from its parent. There are usually two parts to this --

  • You have an init function to initialize child component state. This can be as flexible as you need it to be to specify what state should be passed in and what should be defaulted within the child, etc.
  • From the parent component, you expose to the child component how to update the branch(es) of the state tree that is/are to get updated by child actions. This is harder to get your head around, and seems verbose and tedious at first -- at least for me coming from Om, where persistent data structures + global cursors handle some of the plumbing for you. But the benefit is that you don't have to worry about changing your state tree's structure down the road and having to fix a bunch of components. The Todo app example here has a number of different examples of this, which make use of flyd (similar to Elm's signals), and in particular flyd-forwardto.

The proof is in the pudding though, and I have yet to build anything 'real' with this architecture, although I hope too soon.

I would like to experiment some more with building some useful components which involve both HTTP interactions and also a good bit of 'transient' state. Two things that come to mind are:

  • autocomplete
  • file uploader

NB. I noticed that evancz recently proposed a new set of abstractions for doing HTTP in Elm which I also want to look at more in depth.

@ericgj
Copy link
Contributor

ericgj commented Sep 18, 2015

FYI I've added two examples here, both of which deal with transient state in nested components and asynchronous updates, in case useful to look at (comments welcome).

They both make use of a similar abstraction to Elm's Effects.

On another note, I found this in-depth Redux tutorial useful in terms of thinking about modelling state transitions from the beginning in a moderately complex app (see "Designing The Application State Tree").

@paldepind
Copy link
Owner

They are great examples 👍

I'll have to take a look at the Redux tutorial. Flux has a lot in common with the Elm architecture. Though generally it doesn't seem to insist so much on immutability and purity.

@anthonyshort
Copy link
Author

Awesome, going to have a read through all of this :) Learning a lot here, thanks everyone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants