
Architecting UIs for Change - HenrikJoreteg
https://joreteg.com/blog/architecting-uis-for-change
======
lioeters
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.

~~~
dovel
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?

~~~
pests
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.

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

------
sktrdie
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://lmatteis.github.io/react-behavioral/)

\- [https://medium.freecodecamp.org/an-intro-to-behavioral-
progr...](https://medium.freecodecamp.org/an-intro-to-behavioral-programming-
with-react-request-wait-and-block-ad876e2d235e)

And also have given a talk about how UIs can better align with requirements:
[https://vimeo.com/298554103](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.

------
snorremd
re-frame ([https://github.com/Day8/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.

~~~
HenrikJoreteg
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.

------
aliswe
"If you don't actively fight for simplicity in software, complexity will win"

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

~~~
HenrikJoreteg
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?

~~~
janic
Artsy is a great example since their code is open source
[https://github.com/artsy/emission](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.

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

------
revskill
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".

------
Volrath89
What would be the difference (if any) from the selector concept presented in
the article vs the reducer pattern in redux?

~~~
quonn
The reducer persists the state in the store, the selector projects the store.

~~~
HenrikJoreteg
Another way to put it selectors are a particular view of the data that's in
the store.

------
anonytrary
TLDR: State is hard, use createSelector from Redux and read my book which
explains it in depth.

~~~
uxcolumbo
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

