React Tips & Patterns

mychi_darko

Michael Darko

Posted on March 24, 2021

React Tips & Patterns

React is pretty easy to learn if you know JavaScript, however, it's pretty easy to lose track of your project or just mess things up as it scales or gets ready for a refactor or re-write. I'll share some tips which have literally saved my life...and a whole lot of time😇. Let's get into it!

Tip 1: (Using Containers)

It's very easy to bloat your components with a lot of code: API calls, form logic and a whole lot more logic. To add to all this, the UI code is shoved into these already bloated components. How do we solve this? Containerizing! Containers allow us to isolate our logic and UI code into different components which helps us avoid bloating that particular component just like MVC does. Let's look at an example:

This component fetches news items and displays a UI for the fetched new items

const Dashboard = () => {
  const [news, newsError] = useCustomFetch("/news");
  const [user, userError] = useCustomFetch("/user");
  const [trends, trendsError] = useCustomFetch("/trends");
  const [notifications] = useCustomFetch("/notifications");

  if (news) {
    // sort news for tags
    // sort news for "sort options"
    // perform some custom operations on news
    // do something else like caching?
  }

  if (trends) {
    // sort trends for tags
    // sort trends for "sort options"
    // perform some custom operations on trends
    // do something else like caching?
  }

  if (notifications) {
    // sort notifications for tags
    // sort notifications for "sort options"
    // perform some custom operations on notifications
    // do something else like caching?
  }

  return (
    <div>
      <h2>user</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>notifications</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>Latest News</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>Trends</h2>
      loading handler
      map cards
      display available tags
      display sort options
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

We're skipping a whole lot of logic and UI code here, but you can pretty much see how huge our component can get if left to grow on its own terms. Now let's look at this same example containerized.

Instead of having our entire code as just Dashboard, we can split it into DashboardContainer and Dashboard. It's NOT compulsory to name your containers with Container, however, it's a good naming convention as done with Controllers in MVC eg: UsersController.

DashboardContainer.jsx

const DashboardContainer = () => {
  const [news, newsError] = useCustomFetch("/news");
  const [user, userError] = useCustomFetch("/user");
  const [trends, trendsError] = useCustomFetch("/trends");
  const [notifications] = useCustomFetch("/notifications");

  if (news) {
    // sort news for tags
    // sort news for "sort options"
    // perform some custom operations on news
    // do something else like caching?
  }

  if (trends) {
    // sort trends for tags
    // sort trends for "sort options"
    // perform some custom operations on trends
    // do something else like caching?
  }

  if (notifications) {
    // sort notifications for tags
    // sort notifications for "sort options"
    // perform some custom operations on notifications
    // do something else like caching?
  }

  return (
    <Dashboard
      notifications={notifications}
      trends={trends}
      news={news}
      user={user}
      {/* all your other props */}
    />
  );
};
Enter fullscreen mode Exit fullscreen mode

Now, your dashboard component will look like this:

const Dashboard = ({ user, notifications, ... }) => {
  return (
    <div>
      <h2>user</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>notifications</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>Latest News</h2>
      loading handler
      map cards
      display available tags
      display sort options

      <h2>Trends</h2>
      loading handler
      map cards
      display available tags
      display sort options
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

This way, you can have all your logic in one component and pass all data needed in the UI through props.

Tip 2: (Tidy man's props😂)

I gave this tip a such a ridiculous name because I actually discovered this while I was trying to beautify my code and cut down a bunch of lines. What does this whole thing involve? Let's take a look. In the above tip, we passed our props like this:

<Dashboard
  notifications={notifications}
  trends={trends}
  news={news}
  user={user}
/>
Enter fullscreen mode Exit fullscreen mode

This is fine, but sometimes, you just need something a bit straightforward and easier to grasp. We can replace the above code with this:

const props = { notifications, trends, news, user };

<Dashboard {...props} />
Enter fullscreen mode Exit fullscreen mode

Clean, simple and very readable😊

Tip 3: (Error Boundaries)

According to the react docs, Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

Basically, a part of your app crashing won't drag the whole app down with it, and on top of that, you get to display a custom fallback UI and log/report the errors associated with your app crash. All you need to do is to create your error boundary and pass your components as props. I usually wrap my whole app with the error boundary.

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}
Enter fullscreen mode Exit fullscreen mode

And wrap the component you want to "protect"

<ErrorBoundary>
  <App />
</ErrorBoundary>
Enter fullscreen mode Exit fullscreen mode

That's all. You can check out the docs demo here.

Tip 4: (Picking your libraries)

Like it or not, libraries determine how you write and organize your code. You might have a way of doing something, but a library will ultimately determine what input it takes in and how it works.

One problem I've always had with react is how other libraries usually don't fit into your react app, require a lot of boilerplate code, or how they just have these weird operations😓 Redux meets all these criteria btw😓

There's some good news though, there's usually always an easier/smaller option if you look hard enough. For example, most projects don't need all of redux's features, just a global state, maybe reducers, a setter and a getter😅 You can try libraries like Zustand, Reactn and the multipurpose React Query.

If you want a simpler routing experience, you can also try out Glass Router which takes a friendlier approach to the whole routing business.

Just remember, the community always has simpler, smaller and usually faster alternatives.

Tip 5: (Relative imports)

This applies to CRA users

We usually have different directories for assets, views and all those in our app. This usually leads to uncomfortable imports with ../../... There are a bunch of solutions for this, however, the most used, which I also prefer is to reconfigure webpack to use relative paths: Instead of ../../assets, we can have @/assets

setup

We basically want to edit our CRA setup without having to eject first. There are some nice libraries for this, which we'll install in our project:

yarn add react-app-rewired customize-cra
Enter fullscreen mode Exit fullscreen mode

From there, we create a config-overrides.js file and dump this code in:

const { override, addWebpackAlias } = require("customize-cra");
const path = require("path");

module.exports = override(
    addWebpackAlias({
        ["@"]: path.resolve(__dirname, "src"),
    })
);
Enter fullscreen mode Exit fullscreen mode

From there, we head over to our package.json scripts section and replace react-scripts with react-app-rewired like so:

"scripts": {
  "start": "react-app-rewired start",
  "build": "react-app-rewired build",
  "test": "react-app-rewired test",
  "eject": "react-scripts eject"
}
Enter fullscreen mode Exit fullscreen mode

That's it for the CRA + JS users!

If you're using TypeScript with CRA, you need to add the following so the compiler doesn't shout at you for using @ in your imports.

Create a new file like tsconfig.base.json in your project root (at the same level as your package.json) and add the following:

{
    "compilerOptions": {
        "paths": {
            "@/*": [
                "src/*"
            ]
        }
    },
}
Enter fullscreen mode Exit fullscreen mode

We're not adding this in the main tsconfig.json because TypeScript will rewrite the tsconfig.json and throw this error:

The following changes are being made to your tsconfig.json file:
  - compilerOptions.paths must not be set (aliased imports are not supported)
Enter fullscreen mode Exit fullscreen mode

Now to get this to work, you simply need to extend this in your main tsconfig.json file:

{
  "extends": "./tsconfig.base.json",
Enter fullscreen mode Exit fullscreen mode

You may need to restart your editor for this to take effect (TypeScript users only). From there, you can start replacing all your uncomfortable imports😇

Thanks for reading

These are a few tips and tricks which have helped me speed up my workflow, keep my code neat and basically help on my quest for laziness😇

If you have anything you'll like to share, a new tip, a faster way to do something I mentioned, something you don't agree with, just reach out to me. Thanks!

💖 💪 🙅 🚩
mychi_darko
Michael Darko

Posted on March 24, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related