My mental model of React
Like many programmers, I started my career more comfortable on the back end. Unlike many, I now love React.1 Here are some things I've found myself repeating to colleagues who don't feel comfortable on the front end.
- One-way data flow (see here) is the single most important conceptual point to internalize. There are many, many React concepts, many of which you can defer studying, but do your best to internalize this one.
- Because the data flow works like this, you can think of a React component (which can be a whole page) as a tree (in the graph-theory sense), with the graph relationship defined by the parent-child React-component relationship.
- That doesn't mean that child components can't affect parents; it just means that this happens by means of an object passed from the parent (e.g., a callback).
- The tree-structured, one-way paradigm is a good thing that encourages healthy programs. It's far better to embrace this than to fight against it. Those who complain that the React paradigm is stifling would often be much better off "stifled."
- In particular, don't be shy about factoring out components, even for things (buttons, banner elements, or whatever else) that might seem too small and/or too entwined with nearby elements to warrant it. Making the dependencies and data flows explicit is valuable...
- ...even if you're not yet reusing the component. Here as elsewhere, encapsulation has value far beyond enabling reuse. Suppose, for example, that the "submit" component on your sign-up screen indicates whether form data is valid and includes a link to a status page. The link can often "live in" the component, with no dependency (other than perhaps a way to retrieve the current URL for a given logical name2 of a resource); the form data will need to live in a parent component and be passed in. Forcing yourself to decide where in the tree to keep this information is healthy and will lead to better systems.
- Components should not close over any variables; this breaks encapsulation. (Any good linter will complain about this; do not disable this lint rule.) The convention of putting each component in its own file is another good way to enforce this.
- In general, information should live at the lowest common ancestor of the components that need it.
- When in doubt, it's usually better to put information higher in the tree. The costs tend to be low ("prop drilling" is not so bad, especially if you have LLMs to help you), and having more information available at a node often enables powerful, user-friendly possibilities that might not even occur to you otherwise.
- Speaking of prop drilling not being all that bad: use contexts only with a very good reason. "I don't feel like passing the data down explicitly" is not a good reason.
There's much more to say about React, but if you learn to reason in terms of a tree structure of components, you'll be in a good position to write healthy code.