As React continues to evolve, understanding and implementing advanced patterns can set your applications apart. This post covers some key patterns that enhance your React applications: custom hooks, the Context API, render props, and React Suspense with Concurrent Mode.
Custom hooks allow you to encapsulate logic and reuse it across components without altering their structure. Here's a practical example:
Manage form state across multiple components using a custom hook:
import { useState } from "react";
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (event) => {
const { name, value } = event.target;
setValues((prevValues) => ({
...prevValues,
[name]: value,
}));
};
return [values, handleChange];
}
This hook makes managing form state straightforward:
function ContactForm() {
const [formValues, handleChange] = useForm({ name: "", email: "" });
return (
<form>
<input
type="text"
name="name"
value={formValues.name}
onChange={handleChange}
/>
<input
type="email"
name="email"
value={formValues.email}
onChange={handleChange}
/>
</form>
);
}
The Context API is a powerful tool for managing global state without prop drilling. Here's how to use it effectively:
Create a context to manage and provide theme information:
import React, { createContext, useState, useContext } from "react";
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
return useContext(ThemeContext);
}
Access and modify the theme from any component:
function ThemeSwitcher() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Switch to {theme === "light" ? "dark" : "light"} mode
</button>
);
}
Render props allow you to share logic between components using a function as a child. Here's a simple example:
Track mouse position and share it with a child component:
function MouseTracker({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (event) => {
setPosition({ x: event.clientX, y: event.clientY });
};
return <div onMouseMove={handleMouseMove}>{render(position)}</div>;
}
// Usage
<MouseTracker
render={(position) => (
<p>
Mouse position: {position.x}, {position.y}
</p>
)}
/>;
React Suspense and Concurrent Mode offer a sophisticated approach to handling asynchronous operations. Here’s how to use them together for data fetching:
Fetch user data and display it using React Suspense and Concurrent Mode:
import React, { Suspense } from "react";
const UserData = React.lazy(() => {
const userId = 1; // Example user ID
return new Promise((resolve) => {
fetch(`/api/user/${userId}`)
.then((response) => response.json())
.then((data) => {
resolve({ default: () => <div>User: {data.name}</div> });
});
});
});
function App() {
return (
<Suspense fallback={<div>Loading user data...</div>}>
<UserData />
</Suspense>
);
}
Mastering these advanced React patterns can greatly enhance your development process, leading to cleaner, more maintainable, and performant applications. Implement these techniques to take your React projects to the next level.