React Design Patterns You Cannot Miss

React Design Patterns You Cannot Miss

Here is a list of design patterns in React I have picked up over the years working with React based technologies.

High Order Components

These takes a component as a prop, applies some logic and returns the same component with extra properties.

It is used when we have to apply same logic to multiple components.

Example:

// Comp1.tsx

import { forwardRef } from "react";
import WithDimension from "./WithDimension";

const Component = (props, ref) => {
  return (
    <div ref={ref} className="comp1">
      Hey I am comp1 width: {props.width}
    </div>
  );
};

export default WithDimension(forwardRef(Comp1));
// withDimensions.tsx

import { useEffect, useRef, useState } from "react";

const withDimension = (Element) => {
  function WithDimensions(props) {
    const compRef = useRef();
    const [width, setWidth] = useState(null);
    const [height, setHeight] = useState(null);

    useEffect(() => {
      if (compRef.current) {
        setWidth(compRef.current.offsetWidth);
        setHeight(compRef.current.offsetHeight);
      }
    }, [compRef]);
    return <Element ref={compRef} width={width} height={height} {...props} />;
  }
  return WithDimensions;
};

export default withDimension;

Note: To use ref in react component we have to use forwardRef() on the component which is wrapped inside a higher order component.

Render Props Pattern

A prop in component which is a function that returns JSX.

It can be used to make component customizable.

Example:

// App.tsx
import Input from "./Input";

export default function App() {

  const showValue = (value) => <b>The value is {value}</b>;
  const multiplyByTen = (value) => <>The multiplied value is {value * 10}</>;

  return (
    <div className="App">
      <Input renderTextBelow={showValue} />
      <br />
      <Input renderTextBelow={multiplyByTen} />
    </div>
  );
}
// Input.tsx
import { useState } from "react";

const Input = (props) => {
  const [value, setValue] = useState(null);
  const handleChange = (e) => {
    setValue(e.target.value);
  };
  return (
    <>
      <input value={value} onChange={handleChange} />
      <br />
      {props.renderTextBelow(value)}
    </>
  );
};

export default Input;

Compound Pattern

Multiple components come together to provide one functionality. Like select and option tag in html are used to create a DropDown. React context is used extensively here.

 // App.tsx

import { useState } from "react";
import "./styles.css";
import Tab from "./Tab";

export default function App() {
  const [currentIndex, setIndex] = useState(0);

  const handleChange = (newIndex) => {
    setIndex(newIndex);
  };

  return (
    <div className="App">
      <Tab value={currentIndex} onChange={handleChange}>
        <Tab.Heads>
          <Tab.Item label={"Tab1"} index={0} />
          <Tab.Item label={"Tab2"} index={1} />
          <Tab.Item label={"Tab3"} index={2} />
        </Tab.Heads>
        <Tab.ContentWrapper>
          <Tab.Content index={0}>
            <h1>I am content 1</h1>
          </Tab.Content>
          <Tab.Content index={1}>
            <h1>I am content 2</h1>
          </Tab.Content>
          <Tab.Content index={2}>
            <h1>I am content 3</h1>
          </Tab.Content>
        </Tab.ContentWrapper>
      </Tab>
    </div>
  );
}
// Tab.tsx

import { createContext, useContext } from "react";
import "./Tab.css";
const TabContext = createContext();

export default function Tab({ children, value, onChange }) {
  return (
    <div>
      <TabContext.Provider value={{ value, onChange }}>
        {children}
      </TabContext.Provider>
    </div>
  );
}
Tab.Heads = ({ children }) => {
  return <div className="heads">{children}</div>;
};

Tab.Item = ({ label, index, children }) => {
  const { value, onChange } = useContext(TabContext);
  const handleClick = () => {
    onChange(index);
  };
  return (
    <div
      onClick={handleClick}
      className={`item ${index === value ? "active" : null}`}
    >
      {label}
    </div>
  );
};

Tab.ContentWrapper = ({ children }) => {
  return <div className="contentWraper">{children}</div>;
};

Tab.Content = ({ children, index }) => {
  const { value } = useContext(TabContext);
  return value === index ? <div>{children}</div> : null;
};

Hooks Pattern

Although Hooks are not necessarily a design pattern, Hooks play a very important role in your application design. Many traditional design patterns can be replaced by Hooks.

Rules:

  • Calls hooks on top level.

  • Call hooks only in React functional components

Example:

// ChatPage.tsx
import useOnline from "./useOnline";

export default function ChatPage() {
  const isOnline = useOnline();

  return isOnline ? "User available for chat" : "User not available for chat";
}
// useOnline.tsx

import { useEffect, useState } from "react";

export default function useOnline() {
  const [isOnline, setOnline] = useState(false);

  useEffect(() => {
    setTimeout(() => {
      setOnline(true);
    }, 3000);
  }, []);
  return isOnline;
}