自定义 Hook
自定义 Hook = 把组件间可复用的“状态逻辑”抽成函数:函数名以 use 开头、内部调用其他 Hooks、返回需要的数据与方法。它复用的是逻辑而非界面——组件还是各自渲染,状态互不共享。
例如“展开/折叠”逻辑要在三处用:写成 useToggle() 后,每个组件一行 const [open, toggle] = useToggle() 就能用。常见轮子还有 useLocalStorage(状态同步进 localStorage)、useFetch(请求 + loading + error)、useWindowSize(窗口尺寸)。
- 自定义 Hook = 以 use 开头、内部用其他 Hooks 的普通函数
- 复用的是逻辑不是状态:各组件各一份数据
- 抽离时机:同一段状态逻辑出现第二次时
HTML
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>自定义 Hook</title> <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> </head> <body> <div id="root"></div> <script type="text/babel"> const { useState, useEffect } = React; // 自定义 Hook:开关逻辑,多个组件可各自复用 function useToggle(initial = false) { const [open, setOpen] = useState(initial); const toggle = () => setOpen(o => !o); return [open, toggle]; } // 自定义 Hook:状态与 localStorage 同步 function useLocalStorage(key, initial) { const [value, setValue] = useState(() => { try { return JSON.parse(localStorage.getItem(key)) ?? initial; } catch (e) { return initial; } }); useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue]; } function Panel({ title }) { const [open, toggle] = useToggle(false); // 复用开关逻辑 return ( <div style={{ padding: 8 }}> <button onClick={toggle}>{open ? '折叠' : '展开'} {title}</button> {open && <p>{title} 的内容:两个面板各自独立。</p>} </div> ); } function App() { const [name, setName] = useLocalStorage('demo-name', ''); return ( <div style={{ padding: 16 }}> <Panel title="面板 A" /> <Panel title="面板 B" /> <hr /> <input value={name} onChange={e => setName(e.target.value)} placeholder="刷新页面仍在" /> </div> ); } ReactDOM.createRoot(document.getElementById('root')).render(<App />); </script> </body> </html>
本节要点自定义 Hook 把状态逻辑抽成 use 函数:复用逻辑不共享状态,是 React 复用的正道。




