Patterns
Source: https://reactpatterns.com/
Last updated
Source: https://reactpatterns.com/
Last updated
The simplest way to declare reusable components.
They're just functions.
Collect props
from the first argument of your function.
Define any number of local variables to do what you need in your function components. Always return your React Component at the end.
Set defaults for any required props
using defaultProps
.
Think of it like the opposite of literal assignment.
Works with Arrays too.
Those three dots (...
) take all the remaining properties and assign them to the object restProps
.
So, what do you do with restProps
once you have it?
Keep reading...
This makes Greeting
super flexible.
We can pass DOM attributes to Greeting
and trust that they'll be passed through to div
.
Avoid forwarding non-DOM props
to components.
Destructuring assignment is popular because it gives you a way to separate component-specific props from DOM/platform-specific attributes.
Components are abstractions. Good abstractions allow for extension.
Consider this component that uses a class
attribute for style a button
.
This works great until we try to extend it with another class.
In this case, delete-btn
replaces btn
.
We can change the order but now the className
will never be anything but btn
.
We need to use destructuring assignment to get the incoming className
and merge with the base className
.
We can do this simply by adding all values to an array and joining them with an space.
if
unless
if-else
React can render children
from most types.
In most cases it's either an array
or a string
.
String
Array
Providing an array as children
is a very common.
It's how lists are drawn in React.
We use map()
to create an array of React Elements for every value in the array.
That's equivalent to providing a literal array
.
This pattern can be combined with destructuring, JSX Spread Attributes, and other components, for some serious terseness.
Here's a component that uses a render callback. It's not useful, but it's an easy illustration to start with.
The component calls children
as a function, with some number of arguments. Here, it's the number 500
.
We get this output.
With this setup, we can use this width
to make rendering decisions.
If we plan to use this condition a lot, we can define another components to encapsulate the reused logic.
Obviously a static Width
component isn't useful but one that watches the browser window is. Here's a sample implementation.
You might create a component designed to apply context
and render its children
.
You're faced with a decision. Wrap children
in an extraneous <div />
or return children
directly. The first options gives you extra markup (which can break some stylesheets). The second will result in unhelpful errors.
It's best to treat children
as an opaque data type. React provides React.Children
for dealing with children
appropriately.
(I'm not sure if this name makes sense)
Buttons are everywhere in web apps. And every one of them must have the type
attribute set to "button".
Writing this attribute hundreds of times is error prone. We can write a higher level component to proxy props
to a lower-level button
component.
We can use Button
in place of button
and ensure that the type
attribute is consistently applied everywhere.
Say we have a button. It uses classes to be styled as a "primary" button.
We can generate this output using a couple single-purpose components.
It can help to visualize this.
Using these components, all of these result in the same output.
This can be a huge boon to style maintenance. It isolates all concerns of style to a single component.
When writing event handlers it's common to adopt the handle{eventName}
naming convention.
For components that handle several event types, these function names can be repetitive. The names themselves might not provide much value, as they simply proxy to other actions/functions.
Consider writing a single event handler for your component and switching on event.type
.
Alternatively, for simple components, you can call imported actions/functions directly from components, using arrow functions.
Don't fret about performance optimizations until you have problems. Seriously don't.
Layout components result in some form of static DOM element. It might not need to update frequently, if ever.
Consider a component that renders two children
side-by-side.
We can aggressively optimize this component.
While HorizontalSplit
will be parent
to both components, it will never be their owner
. We can tell it to update never, without interrupting the lifecycle of the components inside.
Given this reusable CommentList
component.
We can create a new component responsible for fetching data and rendering the CommentList
function component.
We can write different containers for different application contexts.
Let's start with our Greeting
component.
If it gets props.name
, it's gonna render that data. Otherwise it'll say that it's "Connecting...". Now for the the higher-order bit.
This is just a function that returns component that renders the component we passed as an argument.
Last step, we need to wrap our our Greeting
component in Connect
.
This is called "state hoisting". It's accomplished by passing a callback from a container component to a child component.
Name
receives an onChange
callback from NameContainer
and calls on events.
The alert
above makes for a terse demo but it's not changing state. Let's change the internal state of NameContainer
.
The state is hoisted to the container, by the provided callback, where it's used to update local state. This sets a nice clear boundary and maximizes the re-usability of function component.
This pattern isn't limited to function components. Because function components don't have lifecycle events, you'll use this pattern with component classes as well.
(It's best to process the event object on the stateful component)
It's hard to talk about controlled inputs in the abstract. Let's start with an uncontrolled (normal) input and go from there.
When you fiddle with this input in the browser, you see your changes. This is normal.
A controlled input disallows the DOM mutations that make this possible. You set the value
of the input in component-land and it doesn't change in DOM-land.
Obviously static inputs aren't very useful to your users. So, we derive a value
from state.
Then, changing the input is a matter of changing component state.
This is a controlled input. It only updates the DOM when state has changed in our component. This is invaluable when creating consistent UIs.
is a JavaScript feature. It was added to the language in ES2015. So it might not look familia
Destructuring assignment is used a lot in . These component declarations below are equivalent.
There's a syntax for collecting remaining props
into an object.
It's called and looks like this.
Spread Attributes is a feature of . It's a syntax for providing an object's properties as JSX attributes.
Following the example from ,
We can spread restProps
over our <div>
.
Order matters for .
The props.className
being spread is overriding the className
in our component.
To guard from undefined
showing up as a className,
Use .
You can't use if/else statements inside a component declarations. So and are your friends.
React components don't support functions as children
. However, is a pattern for creating components that take functions as children.
To use this component, we give it a .
Many developers favor for this type of functionality. It's a matter of preference.
This is a applied to the practices of style.
"A container does data fetching and then renders its corresponding sub-component. That’s it."—
A is a function that takes and/or returns a function. It's not more complicated than that. So, what's a higher-order component?
If you're already using , these are just generic containers, wrapped up in a function.
This is a powerful pattern for providing fetching and providing data to any number of .
don't hold state (as the name implies).
Events are changes in state. Their data needs to be passed to stateful parents.
is an important pattern to know for use with state hoisting
If you're using for form elements, read about using to move new state up the component tree.