|
1 | | ---- |
2 | | -title: "React: More hooks explained" |
3 | | -pubDate: 2025-08-17T18:00:00Z |
4 | | -tags: ['react'] |
5 | | ---- |
6 | | -React version 18 introduced new hooks, not included in the previous article <a href="/en/blog/react-hooks-explained">React: Hooks explained</a>, which will be explained here: |
7 | | -* <a href="#useformstatus">useFormStatus</a> |
8 | | -* <a href="#useinsertioneffect">useInsertionEffect</a> |
9 | | -* <a href="#usesyncexternalstore">useSyncExternalStore</a> |
10 | | - |
11 | | -## State Hooks |
12 | | -### useFormStatus |
13 | | -This hook allows you to interact with form states. It tracks the status of submissions such as _pending_, _submitted_ or _error_, making them easier to manage in modern React applications, especially with concurrent rendering. |
14 | | - |
15 | | -**MyForm.jsx** |
16 | | -```javascript |
17 | | -import { useFormStatus, useState } from 'react' |
18 | | - |
19 | | -export const MyForm = ({ submit }) => { |
20 | | - const { pending, formStatus, setFormStatus } = useFormStatus() |
21 | | - const [email, setEmail] = useState('') |
22 | | - const [error, setError] = useState(null) |
23 | | - const isPending = formStatus === 'pending' |
24 | | - const isError = formStatus === 'error' |
25 | | - |
26 | | - const handleSubmit = async (event) => { |
27 | | - event.preventDefault() |
28 | | - setFormStatus('pending') |
29 | | - try { |
30 | | - await submit(email) |
31 | | - setFormStatus('submitted') |
32 | | - } catch () { |
33 | | - setFormStatus('error') |
34 | | - setError('Submission error. Please try again.') |
35 | | - } |
36 | | - } |
37 | | - |
38 | | - return ( |
39 | | - <div> |
40 | | - <h1>My Form</h1> |
41 | | - <form onSubmit={handleSubmit}> |
42 | | - <input |
43 | | - type="email" |
44 | | - value={email} |
45 | | - onChange={(event) => setEmail(event.target.value)} |
46 | | - required |
47 | | - /> |
48 | | - <button type="submit" disabled={isPending}> |
49 | | - {isPending ? 'Submitting...' : 'Submit'} |
50 | | - </button> |
51 | | - {isError && <p>{error}</p>} |
52 | | - </form> |
53 | | - </div> |
54 | | - ) |
55 | | -} |
56 | | -``` |
57 | | - |
58 | | -## Effect Hooks |
59 | | -### useInsertionEffect |
60 | | -This hook allows you to execute side effects synchronously, before mutations are committed to the DOM. It's useful when you need to insert something into the DOM, such as adding a stylesheet, before React renders the page. It's part of the low-level API for safely handling DOM mutations. |
61 | | - |
62 | | -**DynamicStylesheet.jsx** |
63 | | -```javascript |
64 | | -import { useInsertionEffect } from 'react' |
65 | | -import { theme } from '@/config' |
66 | | - |
67 | | -export const DynamicStylesheet = () => { |
68 | | - useInsertionEffect(() => { |
69 | | - const styleElement = document.createElement('style') |
70 | | - styleElement.innerHTML = ` |
71 | | - body { |
72 | | - background-color: ${theme === 'dark' ? '#333' : '#FFF'} |
73 | | - color: ${theme === 'dark' ? '#FFF' : '#000'} |
74 | | - } |
75 | | - ` |
76 | | - document.head.appendChild(styleElement) |
77 | | - |
78 | | - return () => { |
79 | | - document.head.removeChild(styleElement) |
80 | | - } |
81 | | - }, [theme]) |
82 | | - |
83 | | - return <div>Content with dynamic styles based on theme</div> |
84 | | -} |
85 | | -``` |
86 | | - |
87 | | -## Hooks for Third-Party Libraries |
88 | | -### useSyncExternalStore |
89 | | -This hook allows you to subscribe to external stores or states outside of React, ensuring consistency when using concurrent rendering. It's designed to work well with libraries like Redux, Zustand, or any other custom store implementation. |
90 | | - |
91 | | -**store.js** |
92 | | -```javascript |
93 | | -import create from 'zustand' |
94 | | - |
95 | | -export const useStore = create((set) => ({ |
96 | | - count: 0, |
97 | | - decrease: () => set((state) => ({ count: state.count - 1 })), |
98 | | - increase: () => set((state) => ({ count: state.count + 1 })), |
99 | | -})) |
100 | | -``` |
101 | | - |
102 | | -**Counter.jsx** |
103 | | -```javascript |
104 | | -import { useSyncExternalStore } from 'react' |
105 | | -import { useStore } from '@/store' |
106 | | - |
107 | | -export const Counter = () => { |
108 | | - const [count, decrease, increase] = useStore((state) => [ |
109 | | - state.count, |
110 | | - state.decrease, |
111 | | - state.increase |
112 | | - ]) |
113 | | - const syncCount = useSyncExternalStore( |
114 | | - useStore.subscribe, |
115 | | - () => count, |
116 | | - () => count |
117 | | - ) |
118 | | - |
119 | | - return ( |
120 | | - <div> |
121 | | - <h1>Count: {syncCount}</h1> |
122 | | - <button onClick={decrease}>Decrease</button> |
123 | | - <button onClick={increase}>Increase</button> |
124 | | - </div> |
125 | | - ) |
126 | | -} |
127 | | -``` |
128 | | - |
| 1 | +--- |
| 2 | +title: "React: More hooks explained" |
| 3 | +pubDate: 2025-08-17T18:00:00Z |
| 4 | +tags: ['react'] |
| 5 | +--- |
| 6 | +React version 18 introduced new hooks, not included in the previous article <a href="/en/blog/react-hooks-explained">React: Hooks explained</a>, which will be explained here: |
| 7 | +* <a href="#useformstatus">useFormStatus</a> |
| 8 | +* <a href="#useinsertioneffect">useInsertionEffect</a> |
| 9 | +* <a href="#usesyncexternalstore">useSyncExternalStore</a> |
| 10 | + |
| 11 | +## State Hooks |
| 12 | +### useFormStatus |
| 13 | +This hook allows you to interact with form states. It tracks the status of submissions such as _pending_, _submitted_ or _error_, making them easier to manage in modern React applications, especially with concurrent rendering. |
| 14 | + |
| 15 | +**MyForm.jsx** |
| 16 | +```javascript |
| 17 | +import { useFormStatus, useState } from 'react' |
| 18 | + |
| 19 | +export const MyForm = ({ submit }) => { |
| 20 | + const { pending, formStatus, setFormStatus } = useFormStatus() |
| 21 | + const [email, setEmail] = useState('') |
| 22 | + const [error, setError] = useState(null) |
| 23 | + const isPending = formStatus === 'pending' |
| 24 | + const isError = formStatus === 'error' |
| 25 | + |
| 26 | + const handleSubmit = async (event) => { |
| 27 | + event.preventDefault() |
| 28 | + setFormStatus('pending') |
| 29 | + try { |
| 30 | + await submit(email) |
| 31 | + setFormStatus('submitted') |
| 32 | + } catch () { |
| 33 | + setFormStatus('error') |
| 34 | + setError('Submission error. Please try again.') |
| 35 | + } |
| 36 | + } |
| 37 | + |
| 38 | + return ( |
| 39 | + <div> |
| 40 | + <h1>My Form</h1> |
| 41 | + <form onSubmit={handleSubmit}> |
| 42 | + <input |
| 43 | + type="email" |
| 44 | + value={email} |
| 45 | + onChange={(event) => setEmail(event.target.value)} |
| 46 | + required |
| 47 | + /> |
| 48 | + <button type="submit" disabled={isPending}> |
| 49 | + {isPending ? 'Submitting...' : 'Submit'} |
| 50 | + </button> |
| 51 | + {isError && <p>{error}</p>} |
| 52 | + </form> |
| 53 | + </div> |
| 54 | + ) |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +## Effect Hooks |
| 59 | +### useInsertionEffect |
| 60 | +This hook allows you to execute side effects synchronously, before mutations are committed to the DOM. It's useful when you need to insert something into the DOM, such as adding a stylesheet, before React renders the page. It's part of the low-level API for safely handling DOM mutations. |
| 61 | + |
| 62 | +**DynamicStylesheet.jsx** |
| 63 | +```javascript |
| 64 | +import { useInsertionEffect } from 'react' |
| 65 | +import { theme } from '@/config' |
| 66 | + |
| 67 | +export const DynamicStylesheet = () => { |
| 68 | + useInsertionEffect(() => { |
| 69 | + const styleElement = document.createElement('style') |
| 70 | + styleElement.innerHTML = ` |
| 71 | + body { |
| 72 | + background-color: ${theme === 'dark' ? '#333' : '#FFF'} |
| 73 | + color: ${theme === 'dark' ? '#FFF' : '#000'} |
| 74 | + } |
| 75 | + ` |
| 76 | + document.head.appendChild(styleElement) |
| 77 | + |
| 78 | + return () => { |
| 79 | + document.head.removeChild(styleElement) |
| 80 | + } |
| 81 | + }, [theme]) |
| 82 | + |
| 83 | + return <div>Content with dynamic styles based on theme</div> |
| 84 | +} |
| 85 | +``` |
| 86 | + |
| 87 | +## Hooks for Third-Party Libraries |
| 88 | +### useSyncExternalStore |
| 89 | +This hook allows you to subscribe to external stores or states outside of React, ensuring consistency when using concurrent rendering. It's designed to work well with libraries like Redux, Zustand, or any other custom store implementation. |
| 90 | + |
| 91 | +**store.js** |
| 92 | +```javascript |
| 93 | +import create from 'zustand' |
| 94 | + |
| 95 | +export const useStore = create((set) => ({ |
| 96 | + count: 0, |
| 97 | + decrease: () => set((state) => ({ count: state.count - 1 })), |
| 98 | + increase: () => set((state) => ({ count: state.count + 1 })), |
| 99 | +})) |
| 100 | +``` |
| 101 | + |
| 102 | +**Counter.jsx** |
| 103 | +```javascript |
| 104 | +import { useSyncExternalStore } from 'react' |
| 105 | +import { useStore } from '@/store' |
| 106 | + |
| 107 | +export const Counter = () => { |
| 108 | + const [count, decrease, increase] = useStore((state) => [ |
| 109 | + state.count, |
| 110 | + state.decrease, |
| 111 | + state.increase |
| 112 | + ]) |
| 113 | + const syncCount = useSyncExternalStore( |
| 114 | + useStore.subscribe, |
| 115 | + () => count, |
| 116 | + () => count |
| 117 | + ) |
| 118 | + |
| 119 | + return ( |
| 120 | + <div> |
| 121 | + <h1>Count: {syncCount}</h1> |
| 122 | + <button onClick={decrease}>Decrease</button> |
| 123 | + <button onClick={increase}>Increase</button> |
| 124 | + </div> |
| 125 | + ) |
| 126 | +} |
| 127 | +``` |
| 128 | + |
129 | 129 | These hooks provide a more controlled way to interact with the React lifecycle and external state, making it easier to manage complex, concurrent applications. |
0 commit comments