I removed all fancy words and just focus on the idea so its not gonna be so “professional”~
Presentational and Container Component Pattern
Provider Pattern
Compound Components Pattern
The higher-order component pattern
Well,… only display the UI, but also have its own state
const ItemsList = (props) => {
return (
<ul>
{props.items.map((item) => (
<li key={item.id}>
<a href={item.url}>{item.name}</a>
</li>
))}
</ul>
);
};
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, responsible for managing its state.
Control how things work together, contain lifecycle methods and Presentational components. It is 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>
);
}
}
Do note that Dan also mentions that he’s no longer promoting this pattern as he’s changed his view on the matter since he originally coined it. However, you might find it useful for your particular use case which is why I thought it relevant to be mentioned on this list.
Prop drilling is bad, better do Context Provider/Store or Redux
Context remind:
import { createContext } from "react";
const ThemeContext = createContext({
theme: "light",
setTheme: () => {},
});
export default ThemeContext;
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>
);
}
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 is advised to use this approach sparingly because it makes component reuse difficult
Peer to peer components, must have all components to work, ex: Menu + Menu.Item
Code:
Menu:
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>
);
};
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>
);
};
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>
);
}
Use when want to sharing of component logic across our application. Examples of these features are authorization, logging, and data retrieval.
HOCs are not part of the core React API, but they arise from the compositional nature of React functional components, which are JavaScript functions.
Code:
import React, { Component } from "react";
const higherOrderComponent = (DecoratedComponent) => {
class HOC extends Component {
render() {
return <DecoratedComponent />;
}
}
return HOC;
};
https://blog.openreplay.com/3-react-component-design-patterns-you-should-know-about https://blog.logrocket.com/react-component-design-patterns-2022/