DEV Community

Cover image for React Enterprise Design Patterns
Ashwan Lal
Ashwan Lal

Posted on

React Enterprise Design Patterns

React is a powerful JavaScript library for building user interfaces. Its component-based architecture and ability to build reusable components have made it the go-to choice for many enterprise-level applications. However, as the application grows in size and complexity, it becomes more challenging to maintain and scale. This is where design patterns come into play. In this blog post, we will explore some of the most commonly used React enterprise design patterns, along with code examples in TypeScript.

Container and Presentational Components Pattern

One of the most widely used patterns in React is the Container and Presentational Components pattern. This pattern separates the presentation logic from the business logic, making the code more modular and easier to test. The container component is responsible for fetching data from the server and passing it down to the presentational component as props. The presentational component is responsible for rendering the UI.

// Container Component
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from './userActions';
import { RootState } from './rootReducer';
import UserList from './UserList';

const UserListContainer: React.FC = () => {
  const dispatch = useDispatch();
  const { loading, users } = useSelector((state: RootState) => state.users);

  useEffect(() => {
    dispatch(fetchUsers());
  }, [dispatch]);

  return <UserList loading={loading} users={users} />;
};

export default UserListContainer;

// Presentational Component
import React from 'react';
import { User } from './userTypes';

interface Props {
  loading: boolean;
  users: User[];
}

const UserList: React.FC<Props> = ({ loading, users }) => {
  if (loading) return <div>Loading...</div>;
  if (!users) return null;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

export default UserList;
Enter fullscreen mode Exit fullscreen mode

Render Props Pattern

The Render Props pattern is a technique for sharing code between components using a prop whose value is a function. This pattern is useful when you have components that share similar functionality but have different rendering requirements.

import React from 'react';

interface Props {
  render: (count: number, increment: () => void) => JSX.Element;
}

const Counter: React.FC<Props> = ({ render }) => {
  const [count, setCount] = React.useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  return render(count, increment);
};

export default Counter;

// Usage
import React from 'react';
import Counter from './Counter';

const App: React.FC = () => {
  return (
    <div>
      <h1>Render Props Pattern</h1>
      <Counter
        render={(count, increment) => (
          <div>
            <p>Count: {count}</p>
            <button onClick={increment}>Increment</button>
          </div>
        )}
      />
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Higher-Order Component Pattern

The Higher-Order Component (HOC) pattern is a design pattern in React that enables component reusability and code sharing by providing a way to enhance or modify a component’s functionality.

In simple terms, an HOC is a function that takes in a component as an argument and returns a new component with additional features. The new component can then be rendered like any other component in React. The original component is wrapped by the HOC, and the HOC provides additional behavior or props to the wrapped component.

HOCs can be used for a wide range of tasks, such as providing context, handling errors, fetching data, or enforcing authentication. They can help to reduce code duplication, increase code maintainability, and promote code reuse.

import React from 'react';

// Define a higher-order component
const withName = (WrappedComponent) => {
  return class extends React.Component {
    state = {
      name: 'John Doe'
    }

    render() {
      return (
        <WrappedComponent name={this.state.name} {...this.props} />
      )
    }
  }
}

// Define a regular component
const MyComponent = (props) => {
  return (
    <div>
      <p>Hello, my name is {props.name}.</p>
      <p>I am a {props.role}.</p>
    </div>
  )
}

// Wrap MyComponent with the withName HOC
const MyComponentWithName = withName(MyComponent);

// Render MyComponentWithName
const App = () => {
  return (
    <div>
      <MyComponentWithName role="developer" />
    </div>
  )
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Disabled prop pattern

The disabled prop pattern is a design pattern used in React that enables the creation of accessible user interfaces by providing a way to disable interactive elements such as buttons, links, and form fields when they are not available or applicable.

The pattern involves adding a disabled prop to the interactive elements in the component, which when set to true, disables the element and prevents it from receiving user input. This is especially useful for situations where the interactive element is not applicable due to a particular condition, such as an incomplete form or insufficient user permissions.

import React from 'react';

const App = () => {
  return (
    <div>
      <SomeComponent disabled />
    </div>
  );
};

const SomeComponent = ({ disabled = false }) => {
  return (
    !disabled && (
      <div>
        <h2>Disable SomeComponent to see magic happen!</h2>
      </div>
    )
  );
};
Enter fullscreen mode Exit fullscreen mode

Controlled and Uncontrolled Component Patterns

A controlled component is a form element whose value is controlled by React. In other words, the value of the component is always set explicitly through props and updated through callbacks. This means that the component’s state is always in sync with the input data, allowing React to control the component’s behavior and allowing the developer to handle user input easily.

import React, { useState } from 'react';

function ControlledInput() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  return (
    <input type="text" value={value} onChange={handleChange} />
  );
}

Enter fullscreen mode Exit fullscreen mode

An uncontrolled component is a form element whose value is managed by the browser. In other words, the value of the component is set by the user, and React does not control its behavior. This can make it more difficult to handle user input in complex forms, but it can also be faster and easier to use in simple forms.

import React, { useRef } from 'react';

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = (event) => {
    event.preventDefault();
    console.log(inputRef.current.value);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" ref={inputRef} />
      <button type="submit">Submit</button>
    </form>
  );
}

Enter fullscreen mode Exit fullscreen mode

Compound Components Pattern

The Compound Components Pattern is a design pattern used in React to manage components that are composed of multiple child components. This pattern involves separating the concerns of the parent component into smaller components, and then using a combination of props, context, and other techniques to manage the relationships between these smaller components.

The idea behind the Compound Components Pattern is to provide a way for developers to create reusable, flexible, and easy-to-use components that are composed of smaller building blocks. This allows developers to create complex UI components that can be easily customized and extended, while still maintaining a clear and easy-to-understand code structure.

import React, { createContext, useState } from 'react';

const ToggleContext = createContext();

function Toggle({ children }) {
  const [on, setOn] = useState(false);

  const toggle = () => setOn(!on);

  return (
    <ToggleContext.Provider value={{ on, toggle }}>
      {children}
    </ToggleContext.Provider>
  );
}

function ToggleOn({ children }) {
  const { on } = useContext(ToggleContext);
  return on ? children : null;
}

function ToggleOff({ children }) {
  const { on } = useContext(ToggleContext);
  return on ? null : children;
}

function ToggleButton(props) {
  const { on, toggle } = useContext(ToggleContext);
  return <button onClick={toggle} {...props} />;
}

function App() {
  return (
    <Toggle>
      <ToggleOn>The button is on</ToggleOn>
      <ToggleOff>The button is off</ToggleOff>
      <ToggleButton>Toggle</ToggleButton>
    </Toggle>
  );
}

Enter fullscreen mode Exit fullscreen mode

Prop Collections Pattern

The Prop Collections Pattern is a design pattern used in React to pass a collection of props to a component. It involves combining multiple related props into a single object, which is then passed to the component as a single prop.

This pattern is particularly useful when we want to pass a large number of related props to a component, as it allows us to reduce the clutter in our code and make it easier to manage the props.

import React from 'react';

function Button(props) {
  const { label, color, size, ...rest } = props;
  return (
    <button style={{ color, fontSize: size }} {...rest}>
      {label}
    </button>
  );
}

function App() {
  const buttonProps = {
    label: "Click Me",
    color: "red",
    size: "20px",
    onClick: () => console.log("Button clicked!")
  };
  return <Button {...buttonProps} />;
}

Enter fullscreen mode Exit fullscreen mode

Function-as-Child Pattern

The Function-as-Child pattern is a design pattern used in React that involves passing a function as a child to a component, which is then called inside the component to render the actual content.

import React from 'react';

function Toggle(props) {
  const [on, setOn] = useState(false);

  const handleToggle = () => {
    setOn(!on);
  };

  return props.children({
    on: on,
    toggle: handleToggle
  });
}

function App() {
  return (
    <Toggle>
      {({ on, toggle }) => (
        <div>
          {on ? "The button is on" : "The button is off"}
          <button onClick={toggle}>Toggle</button>
        </div>
      )}
    </Toggle>
  );
}
Enter fullscreen mode Exit fullscreen mode

Controlled Input Pattern

The Controlled Input pattern is a pattern used in React for managing input fields. It involves storing the current value of an input field in the component state and using an event handler to update the state when the input value changes.

import React, { useState } from "react";

function ControlledInput() {
  const [inputValue, setInputValue] = useState("");

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleInputChange} />
      <p>The input value is: {inputValue}</p>
    </div>
  );
}

Enter fullscreen mode Exit fullscreen mode

Immutable Pattern

The Immutable Pattern is often used to manage the state of components. Instead of modifying the state directly, we create a new state object with the updated values, which is then passed to the component to render.

The Immutable.js library can be used to create immutable data structures, which can be used for React state management. Here’s an example of how the Immutable Pattern can be used in a React component:

import React, { Component } from 'react';
import { Map } from 'immutable';

class MyComponent extends Component {
  state = {
    data: Map({
      name: 'John',
      age: 30,
      email: 'john@example.com'
    })
  };

  handleNameChange = (event) => {
    const name = event.target.value;
    const newData = this.state.data.set('name', name);
    this.setState({ data: newData });
  };

  render() {
    const { data } = this.state;
    return (
      <div>
        <label>Name:</label>
        <input type="text" value={data.get('name')} onChange={this.handleNameChange} />
        <label>Age:</label>
        <span>{data.get('age')}</span>
        <label>Email:</label>
        <span>{data.get('email')}</span>
      </div>
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

Conclusion

Overall, React enterprise design patterns are a valuable set of tools and techniques that can help you build better React applications, improve development productivity and efficiency, and deliver high-quality software that meets the needs of your users and stakeholders.

Resource

https://medium.com/gitconnected/react-enterprise-design-patterns-81f009fbc221

Top comments (0)