React Paradigms in Practice
Table of Contents
- React Components
- Paradigms
- How Can We Leverage These?
- Flexible Layouts with Context API and Render Props
React Components
Paradigms
Introduction and Overview
Here's a quick video discussion about the major patterns of React components that we'll be exploring.
Functional Component (Pure Component)
No state, no side effects, just rendering. Choose your favorite flavor.
What is PureComponent?
A React Component's shouldComponentUpdate method is defaulted to return true, as this method exists to give the developer a means of improving component performance on a case by case basis. PureComponent has this defaulted
to a shallow comparison of props and state, which is largely what you would want in a component that acts like a function (same input, same output). Using Pure Component can improve performance by preventing unnecessary rendering of react components.
Still some discussion about usage
There are some benefits and drawbacks
Quick summary about Functional Components:
- Easy to test
- Easy to read
- Provide no efficiency under the hood, React still creates the class that you didn't
- Conversions to PureComponent (the efficient version) look noisy in Diff viewers as you have to add class boilerplate
- The React Team has plans to make Functional Components implement PureComponent by default.
Additional Resources
A note on Presentational Components (Spoiler alert: It's just a View)
Dan Abramov strongly suggests separating Functional (Presentational) Components and Controller components. The distinction is usually drawn along the lines of presentational components organizing the view, while the container component may load or organize data. So if you use Functional Components as views in MVC, you're in good form!
Sometimes you have complex structures that would be served well by having a layout view component. This would look something like the following:
Controller Component
Can have state, bind functions, organize layouts
It loads data. It might send it to view components. It hooks into the lifecycle methods to get the job done
Higher Order Component
Provides additional functionality to the component(s) that are wrapped
Structure: const HoC = config => BaseComponent => EnhancedComponent
Separating logic from containers can help improve your code
- Containers can worry less about its data loading, management, or connecting to application state management when that functionality is added via an HoC.
- Enables you to share functionality between components, as the HoCs exist independent of the components they are applied to.
- Can make using components more difficult, as you need to look at the
connectfunction to see what props are being passed by Redux and what props are required to be given to the component. - Adds some visual complexity, as now your app has logic being added to compoennts by a function call, which doesn't look very "jsx"-y. You need to hunt down bugs tracking both through layers of JSX and layers of HoCs.
The React team has certain suggestions about how to implement HoCs
- Don't modify the functions of the wrapped component
- HoCs should support passthrough of unrelated props
- Use the HoC thunk pattern to support composition , which just means you have the parenthesis
connect(config)(Component)instead ofconnect(config, Component)- This allows for clear distinction of the component and argmuments when applying multiple HoCs
Additional Resources
- Higher Order Components in the React Documentation
- An introduction to HoCs
- A tutorial on HoCs by example
- The self-purported Ultimate Guide to HoCs
Render Props
Share functionality between components
Here we have a Toggle Render Prop which allows us to share the state of 'on' and a functin to modify that state. Anywhere we need that functionality we can use this Toggle, meaning that we can re-use this state logic!
What exactly do Render Props do for you?
- Share functionality between components
- The component parts that were boilerplate before can now be re-used!
- Ever had a component that loads data on mounting, reloads on specific props changes?
- Maybe it also has a loading state?
- Stay tuned for some examples of how to use these concepts to re-use code to address these problems!
- Represent a way of utilizing depencency injection in combining components
- A
Listcontainer could know nothing about how eachListItemis displayed - You can skip the component creation steps required in HoCs, as Render Props are defined at their usage.
- A
- But make sure you know why you are using a Render Prop
Some things to note
React does not assign meaning to passing a function as children (a function is not a React.Node type). This is awesome! It means that we can co-opt that usage of children for Render Props.
Instead of defining a prop render or component being how the function is passed, you can instead pass the function in as the children prop.
This keeps code that uses Render Props clean and expressive, as you'll see later on. The React Team themselves have
embraced this paradign in the React Context API, which uses Render Props to provide an easy interface to avoid
passing props through multiple layers of components (commonly known as prop drilling).
Render Props and Higher Order Components ultimately achieve the same thing in different ways. Higher Order Components add functionality to the component but can obscure it behind exports. You'll see this with Redux when we export the connected component, export default connect(getCurrentUser)(Header). This can reduce reusability and make testing more difficult, as you either mock the HoC or export both the unconnected and connected versions of the component.
Render Props have drawbacks as well, as they define added functionality in-line, which means adding some complexity to the jsx portions. This is located next to the usage, so you have the choice of where you will look to track down bugs. With HoCs, you'll be looking for the definitions of wrapped components to see what is added. With Render Props, you'll be looking in-line in the jsx.
Unrelated, some people are mad about the name Render Prop, but 'function as a child' is not a great
alternative name. There are concerns about how having children be a renderable component does not follow good
naming conventions, as return this.props.children() does not make a lot of sense from how things are named. If
you agree, you could consider renaming the children prop. This does create a class as jsx is being used, so only
do this if you really disagree with calling children as a function.
Additional Resources
- Introduction to Render Props and why they are useful
- Render Props in the React documentation
How Can We Leverage These?
Let's check out some useful pre-made Render Props from React-Powerplug
Hover
Lets you easily use the hover state on the component.
Value
Store whatever value you want, no need to define state! Oh yea, this Render Prop takes in an onChange callback,
so you can keep track of what's going on. Ever wanted input text to control the text of another component? Well then maybe this the tool for you!
List
Ever wanted an easy way to manipulate a list that's stored in state? Well here's an abstracted way to do that!
List also takes a onChange callback, meaning you can keep your code squeeky clean and still manipulate that list.
State
For when you want to throw the kitchen sink at your problem. If you wanted state… just write a class. But React-Powerplug won't judge you if you use this.
Use Case: Using an Accordion
Sometimes you have a controlled component that you'd rather were uncontrolled, and a handy Render Prop could give the functionality you need in-line.
Defining your own state? Who needs it! We've got Render Props to share the logic, letting us code even faster.
Data Loading
Wouldn't it be nice if there were some sort of Loader component?
Something that handled loading data on mounting, loading when relevant props change, setting a loading state, and handling errors? Something that could be used like this:
Let's look at an HoC implementation of Loader first
The HoC has a pre-determined target for what will be modified by the values
And here's a Render Props version of Loader
Render Props allows you to stay flexible and have an indeterminate target, children. Looks very similar in implementation to the HoC, as they both provide ways to share logic!
Package to help get ideas about how to use Render Props
React-Powerplug
It's only 3kb minified! Has many more great Render Props than just the few examples from above.
List of Render Props Packages
Here is a collection of public packages that leverage Render Props to get the job done. Even if you don't want to write some Render Props yourself, it pays to know the structure as many community packages rely upon it.
Flexible Layouts with Context API and Render Props
How should component data flow be organized to allow flexibility with Render Props or with Context API
If the view is always defined statically, there is little flexibility for re-use. At some point you may want to be able to reuse some code, but shuffle around how the components are being displayed. If the view is defined rigidly, you would either need to create a whole new view for the component or add flags that allow you to switch between layouts.
But wouldn't it be nice if there were a simple, high level way of re-organizing the view that removes data-flow concerns? Let's explore ways to utilize React Context and Render Props to add flexibility to your React App where you may not have thought there was room for any.
Wouldn't it be nice to have this kind of flexibility?
One option is to use render props to pass the needed props to the children components, however this would only look nice in simple cases. In complex cases, you'd just be expanding props into every child that you need to render like this, which is just more boilerplate / noise.
So let's not use render props in our goal-syntax, and instead create something that takes in props at the top level, and allows us to place some view components wherever we want them underneath. This keeps our layout definition nice and clear, and we know that props are 'prop'-erly making it to their final destination.
Let's see how we can create this kind of component utilizing React's context API to allow us to skip directly passing props.
Word of caution
The above is a little limited in re-use, as we're using a statically defined Context Provider / Consumer pair. You should be able to define the Consumer/Provider in the constructor, then injecting the consumer as a dependency to those helper components. But that's an uneeded layer of abstraction for the example above. Think of it as an exercise left to the reader!