Author - StudySection Post Views - 204 views
React

React / ReactJS – Module Design Pattern

React is likely the most well-liked JavaScript library for creating user interfaces, in part because of its impartiality. No matter if you choose to view React as a framework or library, one thing that can be agreed upon is its hands-off approach to how developers should construct React applications, which provides developers and developer teams the freedom to determine how they want their applications to be made. After working on many React projects with various teams and researching previously created React applications, you identify some common design patterns.
In this article, we’ll be looking at three popular design patterns for building React applications.

  1. Presentational and Container Component Pattern

    This is a pattern coined by Dan Abramov. during this pattern, components are divided into:
    Presentation Components: These are components that are liable for how the UI looks. They don’t have any dependencies on any part of the application and are used to display data. An example may be a list:
    const ItemsList = (props) => {
    return (
    <ul>
    {props.items.map((item) => (
    <li key={item.id}>
    <a href={item.url}>{item.name}</a>
    </li>
    ))}
    </ul>
    );
    };

    In the example above, our ItemsList component is merely responsible for displaying the data passed as props on the user interface. Presentational components are also called stateless. functional components but can also be written as class components and can contain state that relates to the UI.
    class TextInput extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    value: ""
    };
    }
    render() {
    return (
    <input value={this.state.value}onChange={(event) => this.setState({ value: event.target.value })}/>
    );
    }
    }

    In the example above, we’ve created a presentational class component, TextInput, liable for managing its state.
    Container Components: Unlike presentational components, container components are skilled in how things work. They’re usually class components that contain lifecycle methods and presentational components. It’s also where data fetching happens.
    class TvShowsContainer extends React.Component {
    constructor(props) {
    super(props);
    this.state = {
    shows: [],
    loading: false,
    error: ""
    };
    }
    componentDidMount() {
    this.setState({ loading: true, error: "" });
    fetch("https://api.tvmaze.com/schedule/web?date=2020-05-29")
    .then((res) => res.json())
    .then((data) => this.setState({ loading: false, shows: data }))
    .catch((error) =>
    this.setState({ loading: false, error: error.message || error })
    );
    }
    render() {
    const { loading, error, shows } = this.state;
    return (
    <div>
    <h1> Tv Shows </h1>
    {loading && <p>Loading...</p>}
    {!loading && shows && <ItemsList items={shows} />}
    {!loading && error && <p>{error}</p>}
    </div>
    );
    }
    }

    We’ve created a TVShowsContainer component that fetches data from an API when the component mounts within the example above. Additionally, it sends that information to the ItemsList presentational component that we earlier created. The separation of concerns and component reuse are advantages of this pattern. Other container components can reuse the ItemList presentational component to display data since it doesn’t tightly include the TvShowsListContainer. You’ll view the working application here.
    Do note that Dan also mentions that he’s not promoting this pattern as he’s changed his view on the matter since he originally coined it. However, you would possibly find it useful for your particular use case which is why I thought it relevant to be mentioned on this list.

  2. Provider Pattern

    Prop drilling is a huge issue for React developers. Prop drilling may be a scenario in which data (props) is passed down to different components until it gets to the component where the prop is needed. While prop-drilling isn’t bad, it becomes an issue when unrelated components share data, which brings us to the Provider pattern. The Provider pattern allows us to store data in a central location, e.g., the React Context object, and therefore the Redux store. Any component that requires this information can then receive it directly from the Context Provider/Store without probing any props.
    Imagine implementing dark mode for an internet app and making unrelated components respond to a theme change triggered by a different component. We will achieve that using the Provider pattern. We create a React context object for storing the worth of the theme.
    import { createContext } from "react";
    const ThemeContext = createContext({
    theme: "light",
    setTheme: () => {}
    });
    export default ThemeContext;

    In the App.js file, we wrap imported components with ThemeContext.Provider. This gives the different components, and their children access to the Context object created

    import React, { useState, useMemo } from “react”;
    import Header from “./Header”;
    import Main from “./Main”;
    import ThemeContext from “./context”;
    import “./styles.css”;
    export default function App() {
    const [theme, setTheme] = useState(“”);
    const value = useMemo(() => ({ theme, setTheme }), [theme]);
    return (
    <ThemeContext.Provider value={value}>
    <div className=”container”>
    <Header />
    <Main />
    </div>
    </ThemeContext.Provider>
    );
    }
    ThemeContexts cannot be modified by default because they are stateless. To unravel this, we will connect the ThemeContext to a state and provide an update function in the ThemeContext to modify the state.
    To access ThemeContext within the components, we will make use of the useContext hook introduced in React 16.9.
    import { useContext } from "react";
    import ThemeContext from "./context";
    const Header = () => {
    const { theme, setTheme } = useContext(ThemeContext);
    const toggleTheme = () => {
    if (theme === "dark") {
    setTheme("");
    return;
    }
    setTheme("dark");
    return;
    };
    return (
    <header className={theme === "dark" && "dark"}>
    <h1> Tv Shows </h1>
    <button onClick={toggleTheme}>Toggle Theme</button>
    </header>
    );
    };
    export default Header;
    import { useContext } from "react";
    import ThemeContext from "./context";
    const Main = () => {
    const { theme } = useContext(ThemeContext);
    return (
    <main className={theme === "dark" && "dark"}>
    <h2>{" "} {theme === "dark" ? "Dark theme enabled" : "Light theme enabled"}</h2>
    </main>
    );
    };
    export default Main;

    While Context makes it easier to pass data among components, it’s advised to use this approach sparingly because it makes component reuse difficult. You’ll access the working app of the example above here. The Provider pattern is employed in React Router and React-Redux.

  3. Compound Components Pattern

    Compound components are components that share a state and work together to achieve a common goal. An example is the <select> and <option> HTML elements. When combined, they create a drop-down menu, but they don’t achieve much on their own.

    The Compound Components pattern is used in popular React UI libraries, e.g. Ant Design and Material UI. Below is an implementation of the Menu component in Material UI
    import * as React from 'react';
    import Menu from '@mui/material/Menu';
    import MenuItem from '@mui/material/MenuItem';
    export default function MaterialMenu() {
    return (
    <div>
    <Button> Menu </Button>
    <Menu>
    <MenuItem>Profile</MenuItem>
    <MenuItem>My account</MenuItem>
    <MenuItem>Logout</MenuItem>
    </Menu>
    </div>
    );
    }

    Without compound components, we’ll have had to pass props to the parent component, then the parent component passes the data down to child components
    <Menu items={['Profile','My account', 'Logout']} />
    The above looks simple, but we start having problems passing more props right down to the child component. for instance, imagine we wanted a default selected menu item
    <Menu items={['Profile', 'My account', 'Logout']} defaultSelected={1} />
    As more requirements are available, the component starts becoming messy and unusable. A simpler method to do this is by using the compound component pattern.
    There are two ways to create a React component using the compound component pattern approach:

    • React.cloneElement
    • React Context

    I’ll be using the React Context approach for the instance below
    import {
    createContext,
    useState,
    useCallback,
    useMemo,
    useContext
    } from "react";
    import "./styles.css";
    const MenuContext = createContext();
    const Menu = ({ children, defaultSelected }) => {
    const [selectedItem, setSelectedItem] = useState(defaultSelected);
    const toggleSelectedItem = useCallback(
    (item) => {
    if (item !== selectedItem) {
    setSelectedItem(item);
    return;
    }
    selectedItem("");
    },
    [selectedItem, setSelectedItem] );
    const value = useMemo(
    () => ({
    toggleSelectedItem,
    selectedItem
    }),
    [toggleSelectedItem, selectedItem] );
    return (
    <MenuContext.Provider value={value}>
    <menu className="menu">{children}</menu>
    </MenuContext.Provider>
    );
    };

    Using the createContext function of the React Context API, we’ve built a context object, MenuContext, for the Menu component. This may hold the shared state for the Menu and MenuItem components. We’ve also created a state for a specific menu item. This may allow us to update the context similar to what we did in the Provider Pattern since the Context API is stateless by design.
    The next step is building the MenuItem Component.
    const useMenuContext = () => {
    const context = useContext(MenuContext);
    if (!context) {
    throw new Error(
    "Menu item component cannot be used outside the Menu component."
    );
    }
    return context;
    };
    const MenuItem = ({ value, children }) => {
    const { toggleSelectedItem, selectedItem } = useMenuContext();
    return (
    <button onClick={() => toggleSelectedItem(value)} id={`${value}-menu-item`} className={`menu__item ${selectedItem === value && "active"}`} >{children}</button>
    );
    };

    The first thing done here is creating a custom hook useMenuContext for checking if the MenuItem is used outside the Menu component and throwing an error if that happens. then, we create our MenuItem utilizing the shared state with the Menu component to detect what style to use to a selected MenuItem and change the selected item when a menu item is clicked.
    To wrap up, we connect these components together within the App component.
    export default function App() {
    return (
    <Menu defaultSelected="My account">
    <MenuItem value="Profile">Profile</MenuItem>
    <MenuItem value="My account">My account</MenuItem>
    <MenuItem value="Logout">Logout</MenuItem>
    </Menu>
    );
    }

Conclusion

In this article, we’ve checked out various design patterns to use in building React components that are extensible and reusable. While this is often not an exhaustive list, it applies to most problems you’ll probably encounter when building components.

If you have skills in PHP programming and you want to enhance your career in this field, a PHP certification from StudySection can help you reach your desired goals. Both beginner level and expert level PHP Certification Exams are offered by StudySection along with other programming certification exams.

Leave a Reply

Your email address will not be published.