React Hooks 高级用法与最佳实践

小飞兽 JavaScript 379 次阅读 2026-07-23

useReducer 的高级模式

useReducer 不只是 useState 的替代品,它在复杂状态逻辑中能大幅减少 bug。推荐将相关状态组织在一起,用 reducer 统一管理:

const initialState = { count: 0, step: 1 };

function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + state.step };
case 'decrement':
return { ...state, count: state.count - state.step };
case 'setStep':
return { ...state, step: action.payload };
case 'reset':
return initialState;
default:
throw new Error('Unknown action');
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<p>计数: {state.count}(步长: {state.step})</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
}


useCallback 和 useMemo 的正确使用


不要过度使用 useMemo/useCallback,它们本身也有性能成本。只有在以下情况使用:子组件用 React.memo 包裹且 prop 来自父组件、回调函数作为其他 hooks 的依赖、计算非常耗时。


function Parent() {
const [query, setQuery] = useState('');

const handleSearch = useCallback((term) => {
console.log('搜索:', term);
setQuery(term);
}, []);

const sortedList = useMemo(() => {
return list
.filter(item => item.name.includes(query))
.sort((a, b) => a.name.localeCompare(b.name));
}, [list, query]);

return <ChildComponent onSearch={handleSearch} list={sortedList} />;
}


自定义 Hook 的设计模式


function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});

const setStoredValue = useCallback((newValue) => {
setValue(prev => {
const valueToStore = newValue instanceof Function ? newValue(prev) : newValue;
try {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (e) {
console.warn('localStorage 写入失败:', e);
}
return valueToStore;
});
}, [key]);

return [value, setStoredValue];
}


useEffect 的依赖管理和清理


function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const timer = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => clearTimeout(timer);
}, [value, delay]);

return debouncedValue;
}

function Search() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 300);

useEffect(() => {
if (debouncedQuery) {
fetchResults(debouncedQuery);
}
}, [debouncedQuery]);

return <input value={query} onChange={e => setQuery(e.target.value)} />;
}


useRef 的妙用


function Timer() {
const intervalRef = useRef(null);
const [count, setCount] = useState(0);

useEffect(() => {
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);

return <div>{count}</div>;
}

function AutoFocus() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} />;
}