React is the most used front end library for building modern, interactive front end web apps. It can also be used to build mobile apps.
In this article, we’ll look at ways to organize our component structure to make maintenance easy.
Container and Presentational Components
We should separate our components for doing logic from the ones that are presenting the data. This keeps components small and easy to test. Testing presentational components is easy since all they do is get data and present them.
Container components don’t render anything presentational, so we only have to check the logic. Therefore, testing container components would be easy as we only have to test the logic.
For instance, we can write the following code to organize our components into a container and presentational component:
import React, { useEffect } from "react";
const Result = ({ result }) => {
return <p>{result.name}</p>;
};
export default function App() {
const [result, setResult] = React.useState({});
const getResult = async () => {
const res = await fetch(`https://api.agify.io?name=michael
`);
setResult(await res.json());
};
useEffect(() => getResult(), []);
return <Result result={result} />;
}
In the code above, App
is the container component for Result
. Result
just displayed what’s passed in from App
, while App
has the logic to get the data that we want to display in Result
by passing the retrieved data via props.
Portals
Portals are a way to render children into a DOM node that exists outside the DOM hierarchy of the parent component. This is convenient for creating things like modals and anything that we want to put outside of the normal DOM hierarchy.
We can create a component that mounts a component in a DOM element of our choice as follows:
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="foo"></div>
</body>
</html>
index.html
:
import React from "react";
import ReactDOM from "react-dom";
const Portal = ({ children }) => {
const domNode = document.getElementById("foo");
return ReactDOM.createPortal(children, domNode);
};
export default function App() {
return (
<Portal>
<p>foo</p>
</Portal>
);
}
In the code above, we have the Portal
component that returns:
ReactDOM.createPortal(children, domNode);
The code above mounts our children
into the DOM Node of our choice, which is the div with the ID foo
.
Then in App
, we render the Portal
component with the p element with text ‘foo’.
Therefore, we’ll see the word ‘foo’ display on the screen and the p element with that text is a child of the div with ID foo
instead of being the child of the div with ID root
, which is the mount point of our app.
Code Splitting
By default, a built React app is loaded all at once. This means that no matter how big it is, it’s downloaded and run in the browser as one big package.
Even though lots of people have fast internet now, this may still be a problem for lots of people around the world with slow connections. To make code load in small pieces asynchronously, we can use code-splitting to load a React app in pieces.
We can do code-splitting with Create React App by using the Webpack’s import
function and providing a component to load when the actual component is loading.
For instance, we can use the import
function, the React.lazy
method, and React.Suspense
component to create make components load when it’s needed instead of loading immediately as follows:
Foo.js
:
import React from "react";
export default function Foo() {
return <p>foo</p>;
}
App.js
:
import React, { Suspense } from "react";
const Foo = React.lazy(() => import("./Foo"));
export default function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Foo />
</Suspense>
);
}
In the code above, we moved the component that we want to load on demand into its own module. Then we import it with:
const Foo = React.lazy(() => import("./Foo"));
instead of a normal ES import.
Then in App
, we use the Suspense
component that’s provided by React and pass a fallback component to show when the component is loading into the fallback
prop.
Inside the Suspense
component, we put the component we actually want to show inside.
Therefore, we’ll see ‘Loading…’ when Foo
is loading, then ‘foo’ is shown.
Conclusion
To make testing and maintenance easy, we should organize our code into container components that have logic and presentation components that are only used to display items.
To mount the React component outside of its usual location, we can render portals to mount our React components anywhere we want.
Finally, to make React load in pieces when it’s needed instead of loading everything at once, we can use code-splitting to load components on demand.