自定义 hook 是 React 16.8 之后,在 React 中,最重要的特性,他极大的提高了 React 的开发体验,并且也在后续的发展中,已经成为了整个行业的标杆型解决方案
但是他并不复杂,他仅仅只是一个普通函数封装,不过和封装普通函数相比的区别就在于,他可以在函数内部使用 React 的 Hook,从而实现状态逻辑的复用。
因此,自定义 hook 就是对 useState、useEffect 等 React Hook 的封装,所以它必须运用于组件函数内部
01function Page() {02const [count, setCount] = useState(0)0304return (05<div>06<div>{count}</div>07<button onClick={() => setCount(count + 1)}>click</button>08</div>09)10}
由于该计数器中,使用了 useState 来定义状态,那么,我们就可以单独将该计数器的状态逻辑封装到一个函数中,封装之后的逻辑变成了这样
1function useCount() {2const [count, setCount] = useState(0)3return { count, setCount }4}
在前面的章节中,我们使用 render props 的方式,通过封装了一个 Mouse 组件的方式,来提取了获取鼠标位置信息的能力,在现在的主流开发中,我们应该使用自定义 hook 来实现这个功能
01import useMouse from './mouse'0203export default function App() {04const { x, y } = useMouse()05return (06<div className='flex items-center justify-center p-4'>07<div className='relative group'>08<div className='p-8'>09<header className='flex items-center gap-3 mb-8'>10<div className='w-2 h-2 bg-indigo-500' />11<h1 className='text-[10px] uppercase tracking-[0.4em] font-bold text-slate-400 dark:text-slate-500'>12System Cursor Tracker13</h1>14</header>1516<div className='space-y-6'>17<div className='flex items-baseline justify-between border-b border-slate-100 dark:border-slate-900 pb-2'>18<span className='text-[10px] font-mono text-slate-400 uppercase tracking-widest'>Axis_X</span>19<span className='text-4xl font-black text-slate-900 dark:text-white font-mono tabular-nums tracking-tighter'>20{x.toString().padStart(4, '0')}21</span>22</div>2324<div className='flex items-baseline justify-between border-b border-slate-100 dark:border-slate-900 pb-2'>25<span className='text-[10px] font-mono text-slate-400 uppercase tracking-widest'>Axis_Y</span>26<span className='text-4xl font-black text-slate-900 dark:text-white font-mono tabular-nums tracking-tighter'>27{y.toString().padStart(4, '0')}28</span>29</div>30</div>3132<footer className='mt-8 pt-4 border-t border-slate-50 dark:border-slate-900 flex justify-between'>33<span className='text-[9px] text-slate-300 dark:text-slate-700 uppercase tracking-widest italic'>34Live Feed / Realtime35</span>36<div className='flex gap-1'>37<div className='w-1 h-1 bg-slate-200 dark:bg-slate-800' />38<div className='w-1 h-1 bg-slate-200 dark:bg-slate-800' />39<div className='w-1 h-1 bg-indigo-500 animate-pulse' />40</div>41</footer>42</div>43</div>44</div>45)46}
我们也可以把前面的待办事项列表,通过自定义 hook 重构,通过代码学习
01import TodoHeader from './TodoHeader'02import TodoInput from './TodoInput'03import TodoList from './TodoList'04import TodoStats from './TodoStats'05import useTodos from './useTodos'0607export default function ArrayState() {08const {09todos,10newTodo,11editingId,12editingText,13addTodo,14deleteTodo,15toggleComplete,16startEditing,17saveEdit,18cancelEdit, setNewTodo, setEditingText,19} = useTodos()2021const handleKeyPress = (e: React.KeyboardEvent) => {22if (e.key === 'Enter') {23addTodo()24}25}2627const completedCount = todos.filter(todo => todo.completed).length28const totalCount = todos.length2930return (31<div className="flex flex-col gap-4 p-4">32<TodoHeader33completedCount={completedCount}34totalCount={totalCount}35/>3637<TodoInput38value={newTodo}39onChange={setNewTodo}40onAdd={addTodo}41onKeyPress={handleKeyPress}42/>4344<TodoList45todos={todos}46editingId={editingId}47editingText={editingText}48onToggleComplete={toggleComplete}49onDelete={deleteTodo}50onStartEdit={startEditing}51onSaveEdit={saveEdit}52onCancelEdit={cancelEdit}53onEditingTextChange={setEditingText}54/>5556<TodoStats57totalCount={totalCount}58completedCount={completedCount}59/>60</div>61)62}
在使用自定义 hook 时,我们需要注意一下几点
use 前缀useState、useEffect 等 React Hook 的封装,所以它必须运用于组件函数内部由于自定义 hook 在逻辑复用上的便利性,我们在后续的学习过程中,还会大量的使用自定义 hook,例如我们在前面章节学到的开关思维,就是自定义 hook 的典型应用