While I don't use Redux anymore, one point in the article really resonated with me: having a single source of truth for the entire app state enables you to "run an entire functional application without any visual components having been built whatsoever".
This decoupling of state (and its reducers/actions) from view is a powerful architecutral pattern that I learned from Redux, and I've built every React application (or component) following it. It's separation of concerns at its best, where the heart of the app is about designing data structures and interfaces (as in API), with reusable, composable, and unit-testable states and actions. The UI then becomes just another consumer of the API, so one can develop/rebuild/extend the view as its own separate layer of concern. Looking back, it's hard to believe I was able to keep things organized any other way.
Do you store all UI state in redux as well as all 'resource state'? Phrased as an example... If you store a JSON object that can be edited in your store do you also store a Boolean value for whether the dialog is open that can be used for editing that JSON object?
I agree with that logic, I think I've encountered every one of those cases.
That makes me see that there isn't a "god" object like the article calls it, that holds all of the app state. There's the root state shared throughout the app, and then each page or component can have its own encapsulated state.
Ideally I like to also keep page- or component-specific state+actions decoupled from their views, functional and testable independently - but often I start/keep them in the component class (and maybe soon hooks).
You can also apply the command pattern in some cases and store the undo redo state globally.this at the global store can be generic and the previous specific state will be part of the saved commands. (More applicable to SPAs).
Hmm, typically, I do store "UI state" (or parts of it) in the top app state if that UI state is shared among pages/routes. In your example, if I want that dialog state to be "persistent" - say, the user goes to another route then comes back, if the dialog state should be kept. Otherwise, I store them in the route component's state, which gets created anew every time.
But your question makes me think, if I store any UI state in the app state, have I really decoupled state and view? I'd say yes - since I'd be able to build and test UI state and actions, independent from the view.
Well, at least theoretically, the way I organize state management allows me to pull in "slices" of app state/actions (or even moved into shared libs/modules), to compose a smaller subset for an API or CLI. So the latter can choose to not include any of the UI-related state.
In practice I haven't used it so much, but I like the idea of treating the UI as just another render target / consumer of app state/actions. It enables "remote control" of the app logic, makes testing simpler, and allows reuse for various interfaces like CLI, API, maybe React Native too.
I've been playing around switching web-apps and other user programs between devices on a single-ususer facing kubernetes system. Part of that relies on all state being sent to the server for distribution if a device switch happens.
I also have played around with a wrapper functions and that can bubble any callback/hook up to the data for further processing. The client no longer worries about sending actions. The server spys on client actions and performs actions on their behalf.
Hi, I have exactly the same conclusion as you - you can check my demo todo app - where the UI is an extension to the app. I'm having React and Mustache renders to demonstrate how the view is totally decoupled from the app logic. It have SSR, both for React and Mustache, Service worker for offline usage. Redux for state management. I'm using typescript.
That diagram is wonderful, every part of it makes sense. I also liked how you describe the "lifecycle" step by step. In a way, it made me realize how the Redux pattern fits in with an evolution of MVC.
I think I'm similar to you, in that I've been developing my own solution to manage the whole app lifecycle in a way that makes sense to me, with a library of (mostly) my own modules so I can reuse it across all apps I build.
At the same time, there's value in following "community standards", generic libraries and patterns that most people are familiar with. So, I'm a bit torn on creating my own patterns/utilties versus adapting existing ones that everyone's using.
Thank you so much for checking it out. I'm so happy that diagram makes sense to you.
One of my favorite talks is from Robert Martin, the first link on the bottom of the about.md - when he explains the history of MVC, I realised that the original idea for MVC is exactly what Redux is. But in the years people changed the meaning of MVC and that's why now it seems different. For me, Redux is 1:1 as M from original MVC docs from the 70s.
I'm also torn on using "standards" vs my own implementation. I feel kinda like an outcast, using my own "framework".
Side joke:
With hooks and functional components, the React community is moving in our direction. I cannot wait for the next logical conclusion - Lets move the hooks out of the components :)
I would like to see some of your code, if you like to share it, please send me an email: alex.tzvetanov at gmail com
Thank you again for spending time on my code and thanks for the feedback!
I like this approach. He's essentially using selectors for most of the app logic, which are cached and are what the view components use to derive data. They're cool because they always give you a "fresh look" at what's in state. I find it interesting in his implementation that selectors can also dispatch other actions.
If the author is reading: have you ever read about Behavioral Programming (BP) principles?
BP is still driven by trying to make change easier to implement in a large codebase, so I think there are some high-level similarities with your approach.
re-frame (https://github.com/Day8/re-frame) essentially implements the ideas and patterns presented in the article/post. Of course you would need to learn ClojureScript to use it, but that might be worth it.
I really like the "selector" pattern. Your app state should not have to reflect your UI component tree. Rather you use selectors to subscribe to some part(s) of your application state and derive any necessary values in the subscription/selector function.
Representing your app state as a single source of truth and expressing all changes to state as effect handling functions means you can now easily test your front-end app without involving a dom at all! It is state transitions that are interesting.
Of course there are other options like Redux and Vuex for React and Vue respectively as well as the elm language if you want to go all in on static types and compile time error checking.
Hey, author of the post here... reframe does seem very similar conceptually (I wasn't aware of it, thanks for the link).
Another interesting parallel between the two is that I implement a "loop" mechanism as well. Essentially, if no actions are fired for a while, and APP_IDLE action is dispatched which just updates "appTime" in redux store. As a result any selectors that depend on time as an input will recompute. This allows for actions to be fired over time based on age of the data, etc.
Elm and others are very cool. I just think a pure JS solution is nice, since that's what the browser runs.
I think there actually is an issue with our data models. REST apis have moved a lot of the complexity of data fetching on the client apps since it is basically a 1 to 1 mapping of the database schemas. Making connections between the different data types becomes a frontend concern which is not ideal since frontend has to deal with partial data and full network latency / failures.
Thinking in graphs and query languages like GraphQL makes building for change a lot easier. Your api now defines all the possible data types and their associations (think nodes and edges). This completely eliminates the need to write data management code as a graphql client library can take care of fetching, normalizing and providing the data to components since it knows about the data schema.
Queries can also be composed from fragments which goes very well with the React component model. This allow each component to define the data it needs and then all those fragments can be composed to generate a query. This query will only fetch the exact data it needs for what is displayed wherever the data comes from.
It does move back a bit of the complexity to the backend (defining schema, fetching connections between the different data types) but this is a lot easier to implement since you have full access to all resources. It is also easier to iterate on the implementations since code shipped to client apps can be hard to update when considering mobile apps.
I use graphql extensively as an API for apps I've built this way. But I don't feel like it really fundamentally changes very much in the client (other than much more efficient data fetching). But you still have to compose the data into the view the client is trying to render. Defining data requirements in components is nice in theory, I just personally don't know of any examples where this approach is being used successfully at scale. There may be examples I don't know of. Do you know of any successful large Relay apps?
Artsy is a great example since their code is open source https://github.com/artsy/emission. Facebook does use relay in a lot of products. I’ve also seen a talk about how airbnb uses Apollo in a similar way as relay and collocate fragments with components.
If I take the example in your article I think this model solves the problem nicely. Let’s say you want to include a cart component that was created by a completely different team, in another part of the app, all you have to do is to add the fragment to the query that already exist in your screen and everything just works thanks to composition.
Then when someone adds features to the cart and for example fetch the full list of items then all screens that render that cart component will have their query update accordingly since they include the cart fragment.
We started using ~this selector pattern for timelyapp.com almost two years ago, and have found it tremendously helpful.
Anytime we’re reading data from Redux, it’s via a selector (usually a composition of multiple selectors).
For data fetching, we use a slightly different approach, but same principle: An xhrCacheBuster string that, when changed, fires a thunk action. Query params/payload is determined via a selector in the thunk.
We use similar patterns to “sign” expensive-to-render things, then compare the renderCacheBuster string in shouldComponentUpdate. Same with virtualized lists (named virtualizationCacheBuster)
From my experience, a successful UI doesn't need to map 1-1 to your domain/database models. The principle is "less is more", that means, deliver only enough functionalities for users to complete their process. So, "less is more" just means "less distraction, more focus".
While I don't use Redux anymore, one point in the article really resonated with me: having a single source of truth for the entire app state enables you to "run an entire functional application without any visual components having been built whatsoever".
This decoupling of state (and its reducers/actions) from view is a powerful architecutral pattern that I learned from Redux, and I've built every React application (or component) following it. It's separation of concerns at its best, where the heart of the app is about designing data structures and interfaces (as in API), with reusable, composable, and unit-testable states and actions. The UI then becomes just another consumer of the API, so one can develop/rebuild/extend the view as its own separate layer of concern. Looking back, it's hard to believe I was able to keep things organized any other way.
Do you store all UI state in redux as well as all 'resource state'? Phrased as an example... If you store a JSON object that can be edited in your store do you also store a Boolean value for whether the dialog is open that can be used for editing that JSON object?
My determination is:
Do I want this state to survive a page reload, new browser session, or coming from a bookmark?
The value in a drop-down box in some random form? Non-global state.
The main page active tab that they were probably reading? Global state.
Scroll position? Global.
Tooltip Position? Probably not useful, non-global.
Something like a dialog or modal depends on the situation of how important it was and if the user would expect it to be there.
I agree with that logic, I think I've encountered every one of those cases.
That makes me see that there isn't a "god" object like the article calls it, that holds all of the app state. There's the root state shared throughout the app, and then each page or component can have its own encapsulated state.
Ideally I like to also keep page- or component-specific state+actions decoupled from their views, functional and testable independently - but often I start/keep them in the component class (and maybe soon hooks).
You can also apply the command pattern in some cases and store the undo redo state globally.this at the global store can be generic and the previous specific state will be part of the saved commands. (More applicable to SPAs).
Hmm, typically, I do store "UI state" (or parts of it) in the top app state if that UI state is shared among pages/routes. In your example, if I want that dialog state to be "persistent" - say, the user goes to another route then comes back, if the dialog state should be kept. Otherwise, I store them in the route component's state, which gets created anew every time.
But your question makes me think, if I store any UI state in the app state, have I really decoupled state and view? I'd say yes - since I'd be able to build and test UI state and actions, independent from the view.
Unless you have code that depends on state that supposed to be kept UI-only.
Then when you expand to an API or CLI interface the web state being tracked no longer makes sense and might be extra ram or loading time.
Well, at least theoretically, the way I organize state management allows me to pull in "slices" of app state/actions (or even moved into shared libs/modules), to compose a smaller subset for an API or CLI. So the latter can choose to not include any of the UI-related state.
In practice I haven't used it so much, but I like the idea of treating the UI as just another render target / consumer of app state/actions. It enables "remote control" of the app logic, makes testing simpler, and allows reuse for various interfaces like CLI, API, maybe React Native too.
I do agree for the consistent mental model.
I've been playing around switching web-apps and other user programs between devices on a single-ususer facing kubernetes system. Part of that relies on all state being sent to the server for distribution if a device switch happens.
I also have played around with a wrapper functions and that can bubble any callback/hook up to the data for further processing. The client no longer worries about sending actions. The server spys on client actions and performs actions on their behalf.
I plan on posting a blog post about the his soon.
Hi, I have exactly the same conclusion as you - you can check my demo todo app - where the UI is an extension to the app. I'm having React and Mustache renders to demonstrate how the view is totally decoupled from the app logic. It have SSR, both for React and Mustache, Service worker for offline usage. Redux for state management. I'm using typescript.
https://drmzn-todo-app.herokuapp.com/
https://github.com/max0xff/drmzn-todo-app
https://github.com/max0xff/drmzn-todo-app/blob/master/About....
I would love to hear your feedback :)
That diagram is wonderful, every part of it makes sense. I also liked how you describe the "lifecycle" step by step. In a way, it made me realize how the Redux pattern fits in with an evolution of MVC.
I think I'm similar to you, in that I've been developing my own solution to manage the whole app lifecycle in a way that makes sense to me, with a library of (mostly) my own modules so I can reuse it across all apps I build.
At the same time, there's value in following "community standards", generic libraries and patterns that most people are familiar with. So, I'm a bit torn on creating my own patterns/utilties versus adapting existing ones that everyone's using.
Thank you so much for checking it out. I'm so happy that diagram makes sense to you.
One of my favorite talks is from Robert Martin, the first link on the bottom of the about.md - when he explains the history of MVC, I realised that the original idea for MVC is exactly what Redux is. But in the years people changed the meaning of MVC and that's why now it seems different. For me, Redux is 1:1 as M from original MVC docs from the 70s.
I'm also torn on using "standards" vs my own implementation. I feel kinda like an outcast, using my own "framework".
Side joke: With hooks and functional components, the React community is moving in our direction. I cannot wait for the next logical conclusion - Lets move the hooks out of the components :)
I would like to see some of your code, if you like to share it, please send me an email: alex.tzvetanov at gmail com
Thank you again for spending time on my code and thanks for the feedback!
I like this approach. He's essentially using selectors for most of the app logic, which are cached and are what the view components use to derive data. They're cool because they always give you a "fresh look" at what's in state. I find it interesting in his implementation that selectors can also dispatch other actions.
If the author is reading: have you ever read about Behavioral Programming (BP) principles?
I have written a bit about it here:
- https://lmatteis.github.io/react-behavioral/
- https://medium.freecodecamp.org/an-intro-to-behavioral-progr...
And also have given a talk about how UIs can better align with requirements: https://vimeo.com/298554103
BP is still driven by trying to make change easier to implement in a large codebase, so I think there are some high-level similarities with your approach.
re-frame (https://github.com/Day8/re-frame) essentially implements the ideas and patterns presented in the article/post. Of course you would need to learn ClojureScript to use it, but that might be worth it.
I really like the "selector" pattern. Your app state should not have to reflect your UI component tree. Rather you use selectors to subscribe to some part(s) of your application state and derive any necessary values in the subscription/selector function.
Representing your app state as a single source of truth and expressing all changes to state as effect handling functions means you can now easily test your front-end app without involving a dom at all! It is state transitions that are interesting.
Of course there are other options like Redux and Vuex for React and Vue respectively as well as the elm language if you want to go all in on static types and compile time error checking.
Hey, author of the post here... reframe does seem very similar conceptually (I wasn't aware of it, thanks for the link).
Another interesting parallel between the two is that I implement a "loop" mechanism as well. Essentially, if no actions are fired for a while, and APP_IDLE action is dispatched which just updates "appTime" in redux store. As a result any selectors that depend on time as an input will recompute. This allows for actions to be fired over time based on age of the data, etc.
Elm and others are very cool. I just think a pure JS solution is nice, since that's what the browser runs.
"If you don't actively fight for simplicity in software, complexity will win"
I think there actually is an issue with our data models. REST apis have moved a lot of the complexity of data fetching on the client apps since it is basically a 1 to 1 mapping of the database schemas. Making connections between the different data types becomes a frontend concern which is not ideal since frontend has to deal with partial data and full network latency / failures.
Thinking in graphs and query languages like GraphQL makes building for change a lot easier. Your api now defines all the possible data types and their associations (think nodes and edges). This completely eliminates the need to write data management code as a graphql client library can take care of fetching, normalizing and providing the data to components since it knows about the data schema.
Queries can also be composed from fragments which goes very well with the React component model. This allow each component to define the data it needs and then all those fragments can be composed to generate a query. This query will only fetch the exact data it needs for what is displayed wherever the data comes from.
It does move back a bit of the complexity to the backend (defining schema, fetching connections between the different data types) but this is a lot easier to implement since you have full access to all resources. It is also easier to iterate on the implementations since code shipped to client apps can be hard to update when considering mobile apps.
I use graphql extensively as an API for apps I've built this way. But I don't feel like it really fundamentally changes very much in the client (other than much more efficient data fetching). But you still have to compose the data into the view the client is trying to render. Defining data requirements in components is nice in theory, I just personally don't know of any examples where this approach is being used successfully at scale. There may be examples I don't know of. Do you know of any successful large Relay apps?
Artsy is a great example since their code is open source https://github.com/artsy/emission. Facebook does use relay in a lot of products. I’ve also seen a talk about how airbnb uses Apollo in a similar way as relay and collocate fragments with components.
If I take the example in your article I think this model solves the problem nicely. Let’s say you want to include a cart component that was created by a completely different team, in another part of the app, all you have to do is to add the fragment to the query that already exist in your screen and everything just works thanks to composition.
Then when someone adds features to the cart and for example fetch the full list of items then all screens that render that cart component will have their query update accordingly since they include the cart fragment.
Probably Facebook, but I'm not entirely sure, since they didn't start with GraphQL but rather created it along the way
We started using ~this selector pattern for timelyapp.com almost two years ago, and have found it tremendously helpful.
Anytime we’re reading data from Redux, it’s via a selector (usually a composition of multiple selectors).
For data fetching, we use a slightly different approach, but same principle: An xhrCacheBuster string that, when changed, fires a thunk action. Query params/payload is determined via a selector in the thunk.
We use similar patterns to “sign” expensive-to-render things, then compare the renderCacheBuster string in shouldComponentUpdate. Same with virtualized lists (named virtualizationCacheBuster)
From my experience, a successful UI doesn't need to map 1-1 to your domain/database models. The principle is "less is more", that means, deliver only enough functionalities for users to complete their process. So, "less is more" just means "less distraction, more focus".
What would be the difference (if any) from the selector concept presented in the article vs the reducer pattern in redux?
The reducer persists the state in the store, the selector projects the store.
Another way to put it selectors are a particular view of the data that's in the store.
TLDR: State is hard, use createSelector from Redux and read my book which explains it in depth.
TLDR: State is hard, use createSelector from Redux and read my FREE book online which explains it in depth.
Edit: if you find it valuable you can also buy a digital copy