DEV Community

Cover image for React Tips & Patterns
Michael Darko
Michael Darko

Posted on

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!

Top comments (12)

Collapse
 
briang123 profile image
Brian Gaines

I feel like you could go further and refactor by passing an array of components that you'd want to render into a dashboard container. The container itself could use Flexbox or Grid to stylize the render and not carrying which component is coming in.

Given this, I would create template containers that are specific to its context vs. passing 4 different contexts to a dashboard and having the dashboard responsible for the UI. I feel it would be much more modular as a result.

Collapse
 
mychi_darko profile image
Michael Darko

If I get you right, you mean a different container for every segment, so you don't mix up different contexts, correct? I do that for really big components or when I need to fetch stuff separately or just make a reusable block of code for multiple pages. Otherwise, I just stick to something similar to what I did above.

Collapse
 
briang123 profile image
Brian Gaines

Cool....Yup. Separation of concerns and isolating functionality to its own container that you can just pass around and use wherever.

Collapse
 
prashanthwagle profile image
Prashanth P Wagle • Edited

Great Tips!.
IMO, Tip #1 can be improved futher by using custom hooks. If we have stateful logic that are to be shared, it is better to use a custom hook.

Collapse
 
zaklaughton profile image
Zak Laughton

Came here to say this! Dan Abramov added an update to a blog he wrote recommending this "container" style here, essentially saying that he thinks hooks are now a better solution.

Collapse
 
mychi_darko profile image
Michael Darko

Thanks for the info😊

Collapse
 
ravigithub profile image
Ravi Kasireddy

These are awesome!

Collapse
 
mrezah1 profile image
MrezaH

was great , thank you so much🙏😍

Collapse
 
mendoza profile image
David Mendoza (He/Him)

Loved the post man, just a simple add-on
if someone is using airbnb eslint spreading the props will launch a problem, hopefully someone reads this while looking it up haha

Collapse
 
mychi_darko profile image
Michael Darko

Oh wow, didn't know that. Is there a workaround?

Collapse
 
aalphaindia profile image
Pawan Pawar

These are awesome!

Some comments may only be visible to logged-in visitors. Sign in to view all comments.