Published on

React Pattern Overview

Authors
  • avatar
    Name
    Khánh
    Twitter

React Pattern

Here are some of the most common patterns that I've used in my projects. These patterns can help you to improve the readability, reusability and maintainability your code.

  • HOC pattern
  • Render Props pattern
  • Container Presentational pattern
  • Compound Pattern
  • Hooks Pattern
  • Headless UI

HOC pattern

HOC (Higher Order Component) is a pattern that allows you to reuse code between components. It is a function that takes a component and returns a new component.

I use it more frequency to wrap the component with additional behaviors. This aim to get the input with the component and return the new component with the additional behaviors.

const withLabel = (Component) => {
  return (props) => {
    return (
      <>
        <label>{props.label}</label>
        <Component {...props} />
      </>
    )
  }
}

const Input = ({ label, ...props }) => {
  return <input {...props} />
}

const LabeledInput = withLabel(Input)

Render Props pattern

This is most popular pattern in React. It allows a component to receive a data from a parent component and render it as a child component.

const DataProvider = ({ children }) => {
  return <div>{children}</div>
}

const DataConsumer = ({ children }) => {
  return <div>{children}</div>
}

const Data = () => {
  return (
    <DataProvider>
      <DataConsumer />
    </DataProvider>
  )
}

The prop is the <DataConsumer /> component in this case

Container Presentational pattern

This pattern enforce the separation of concerns by separating into two parts:

  • Container component: care about what to show to the user.
  • Presentational component: care about how to show the data to the user.
// Presentational component
export default function DogImages({ dogs }) {
  return dogs.map((dog, i) => <img src={dog} key={i} alt="Dog" />);
}

// Container component
export default class DogImagesContainer extends React.Component {
  constructor() {
    super();
    this.state = {
      dogs: []
    };
  }

  componentDidMount() {
    fetch("https://dog.ceo/api/breed/labrador/images/random/6")
      .then(res => res.json())
      .then(({ message }) => this.setState({ dogs: message }));
  }

  render() {
    return <DogImages dogs={this.state.dogs} />;
  }
}

Compound Pattern

Organize the component into a group of components that work together.

const ToggleGroup = ({ children }) => {
  const [activeButton, setActiveButton] = useState(null);

  return (
    <div>
      {React.Children.map(children, (child, index) =>
        React.cloneElement(child, {
          isActive: activeButton === index,
          onToggle: () => setActiveButton(index),
        })
      )}
    </div>
  );
};

const ToggleButton = ({ label, isActive, onToggle }) => {
  return (
    <button
      onClick={onToggle}
      style={{ backgroundColor: isActive ? "lightblue" : "white" }}
    >
      {label}
    </button>
  );
};

const App = () => {
  return (
    <ToggleGroup>
      <ToggleButton label="Nút 1" />
      <ToggleButton label="Nút 2" />
      <ToggleButton label="Nút 3" />
    </ToggleGroup>
  );
};

Hooks Pattern

A way to use React Hooks and custom hooks to manage the state and side effects.

const useToggle = () => {
  const [isActive, setIsActive] = useState(false);

  return { isActive, setIsActive };
}

Headless UI

A way to separate the UI and the logic.

const ImportLogic = ({ children, ...props }) => {
  const [isImporting, setIsImporting] = useState(false);

  const handleImport = async () => {
    // Your import logic here
  };

  return children({
    isImporting,
    error,
    handleImport
  });
}

const ImportButton = ({ isImporting, error, handleImport }) => {
  return (
    <div>
      {/* import button UI */}
    </div>
  );
}

const App = () => {
  return (
    <ImportLogic>
      {(
        {isImporting, handleImport, error}
      ) => (
        <ImportButton 
            isImporting={isImporting}
            handleImport={handleImport}
            error={error}
        />
      )}
    </ImportLogic>
  );
}

Conclusion

By using these patterns, you can improve the readability, reusability and maintainability of your code. Combine them together to get the best result.

References