Skip to content

Instantly share code, notes, and snippets.

@jeremyckahn
Last active November 6, 2018 17:06
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeremyckahn/091aff8d1d62f661828c347750ae7644 to your computer and use it in GitHub Desktop.
Save jeremyckahn/091aff8d1d62f661828c347750ae7644 to your computer and use it in GitHub Desktop.
Proposal for a simpler React application architecture

Proposal for a simpler React application architecture

This is a design document for a minimalist React application architecture. Anecdotally, this design is proving to work well for smaller-to-medium scale projects, but it has not been proven to work for large-scale projects because it has not been tested against those yet.

This design is modeled around the official Intro To React guide put out by the React team. In fact, it does not diverge in any significant way from that guide; it simply adds a few explicit constraints to promote simpler implementations.

Examples of this architecture in use:

Key philosophies

1. Think of React as a game engine rather than an application framework

React works like this:

  1. Accept input (e.g. from the user, network or some other external influence).
  2. Modify the app state based on that input.
  3. Render the app state.

...And this just repeats for every tick. This is worth being made explicitly clear, as the following philosophies build on this basic principle.

2. All significant app state lives in the top-level component

When your app state is all on one place, it becomes much easier to find and make sense of. This isn't any different than what is stated in the Intro To React guide, it just expands on it to say "lift the app state to the very top." You should feel free to add as many sub-components under your top-level component as you need (both direct and nested), but by and large they should be solely focused on working with their props as passed down from their parent, rather than their own state.

There are exceptions to this rule, so don't feel bad if you need to break it. Such valid cases include working with cursor state in text inputs, or for when you are designing generic helper libraries such as react-draggable. Aside from edge cases like this, try to keep all of your state in the top-level component and pass it down as props.

3. All event handlers are passed down from the top-level component

Rather than have your event handlers scattered about your codebase, keep them all near and bound to the top-level component and pass them down as props. This yields many of the same benefits as the previous rule, not least of which is knowing where to find things. This also tends to help keep event handlers generic and reusable, and they can easily and directly modify the application state because they belong to the same Object.

4. Shy away from state containers

Robust state containers are valuable and have many valid use cases, but not every project needs one. In many cases, they can create more complexity than they eliminate. As Dan Abramov himself says, You Might Not Need Redux. Given that this architecture is intended for less-than-enterprise-scale projects, Redux doesn't really fit here. That said, there are many excellent ideas that we can borrow from Redux, such as:

These concepts are fully compatible with the other philosophies of this design, and they are consistent with generally good software architecture. They are worth following!

Benefits of this design

By taking established and proven practices and layering on a thin film of additional constraints, we gain a number of advantages.

1. Testability

When all of your state is in one place, it becomes a lot simpler to test. If you need to get your app into a very specific state to make an assertion, a single setState in your beforeEach function will get you most of the way there.

2. Debuggability

One of the hardest parts of debugging an app is finding things. This design provides a consistent place to put all of your business logic: The top-level component. By emphasizing a minimization of state management abstractions, this also makes it easier to step through code and figure out what it is doing.

3. State serialization

If you want to persist your app state to a server or some other store such as localStorage, you just need to JSON.stringify your top-level component's state and save it. If you want to rehydrate your app state later, simply retrieve the JSON state data and use setState on the top-level component.

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