React?useCallback使用方法詳解
1. useCallback 基礎(chǔ)概念
useCallback 是 React 的一個(gè) Hook,用于記憶函數(shù)定義,避免在每次渲染時(shí)創(chuàng)建新的函數(shù)實(shí)例。它在需要將回調(diào)函數(shù)傳遞給經(jīng)過(guò)優(yōu)化的子組件時(shí)特別有用。 當(dāng)state變化的時(shí)候引起組件重新渲染執(zhí)行會(huì)導(dǎo)致某個(gè)方法被反復(fù)創(chuàng)建增加內(nèi)存負(fù)擔(dān),這個(gè)時(shí)候可以使用useCallback將該函數(shù)進(jìn)行緩存,只創(chuàng)建一次
1.1 基本語(yǔ)法
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], // 依賴項(xiàng)數(shù)組 );
同樣的當(dāng)依賴項(xiàng)省略時(shí)組件重新渲染都會(huì)執(zhí)行,當(dāng)依賴項(xiàng)為空數(shù)組的時(shí)候只有組件初始化的時(shí)候會(huì)執(zhí)行一次,數(shù)組里有依賴項(xiàng)的時(shí)候依賴項(xiàng)發(fā)生變化的時(shí)候都會(huì)緩存一次
1.2 與普通函數(shù)的區(qū)別
function ParentComponent() { const [count, setCount] = useState(0); // ? 每次渲染都會(huì)創(chuàng)建新的函數(shù)實(shí)例 const handleClick = () => { console.log('Clicked'); }; // ? 函數(shù)實(shí)例會(huì)被記憶,只在依賴項(xiàng)變化時(shí)更新 const handleClickMemoized = useCallback(() => { console.log('Clicked'); }, []); // 空依賴數(shù)組,函數(shù)永遠(yuǎn)不會(huì)改變 return <ChildComponent onClick={handleClickMemoized} />; }
2. useCallback 配合 React.memo 使用
2.1 基本示例
// 子組件使用 React.memo 優(yōu)化 const ChildComponent = React.memo(function ChildComponent({ onClick }) { console.log("ChildComponent rendered"); return <button onClick={onClick}>Click me</button>; }); // 父組件使用 useCallback function ParentComponent() { const [count, setCount] = useState(0); const [text, setText] = useState(""); // 使用 useCallback 記憶回調(diào)函數(shù) const handleClick = useCallback(() => { setCount(c => c + 1); }, []); // 空依賴數(shù)組,因?yàn)椴灰蕾嚾魏沃? return ( <div> <input value={text} onChange={e => setText(e.target.value)} /> <p>Count: {count}</p> <ChildComponent onClick={handleClick} /> </div> ); }
2.2 帶有依賴項(xiàng)的示例
function SearchComponent({ onSearch }) { const [searchTerm, setSearchTerm] = useState(""); const [searchHistory, setSearchHistory] = useState([]); // 使用 useCallback 記憶搜索函數(shù) const handleSearch = useCallback(() => { if (searchTerm.trim()) { onSearch(searchTerm); setSearchHistory(prev => [...prev, searchTerm]); } }, [searchTerm, onSearch]); // 依賴 searchTerm 和 onSearch return ( <div> <input value={searchTerm} onChange={e => setSearchTerm(e.target.value)} /> <SearchButton onClick={handleSearch} /> <SearchHistory items={searchHistory} /> </div> ); } // 優(yōu)化的子組件 const SearchButton = React.memo(function SearchButton({ onClick }) { console.log("SearchButton rendered"); return <button onClick={onClick}>搜索</button>; }); const SearchHistory = React.memo(function SearchHistory({ items }) { return ( <ul> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); });
3. 實(shí)際應(yīng)用場(chǎng)景
3.1 表單處理
function ComplexForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '' }); // 記憶表單字段更新函數(shù) const handleFieldChange = useCallback((fieldName) => (event) => { setFormData(prev => ({ ...prev, [fieldName]: event.target.value })); }, []); // 不需要依賴項(xiàng),因?yàn)槭褂昧撕瘮?shù)式更新 return ( <form> <FormField label="Name" value={formData.name} onChange={handleFieldChange('name')} /> <FormField label="Email" value={formData.email} onChange={handleFieldChange('email')} /> <FormField label="Message" value={formData.message} onChange={handleFieldChange('message')} /> </form> ); } const FormField = React.memo(function FormField({ label, value, onChange }) { console.log(`${label} field rendered`); return ( <div> <label>{label}</label> <input value={value} onChange={onChange} /> </div> ); });
3.2 列表渲染優(yōu)化
function TodoList() { const [todos, setTodos] = useState([]); // 記憶添加任務(wù)函數(shù) const handleAdd = useCallback((text) => { setTodos(prev => [...prev, { id: Date.now(), text, completed: false }]); }, []); // 記憶切換完成狀態(tài)函數(shù) const handleToggle = useCallback((id) => { setTodos(prev => prev.map(todo => todo.id === id ? { ...todo, completed: !todo.completed } : todo ) ); }, []); // 記憶刪除函數(shù) const handleDelete = useCallback((id) => { setTodos(prev => prev.filter(todo => todo.id !== id)); }, []); return ( <div> <AddTodo onAdd={handleAdd} /> {todos.map(todo => ( <TodoItem key={todo.id} todo={todo} onToggle={handleToggle} onDelete={handleDelete} /> ))} </div> ); } const TodoItem = React.memo(function TodoItem({ todo, onToggle, onDelete }) { return ( <div> <input type="checkbox" checked={todo.completed} onChange={() => onToggle(todo.id)} /> <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}> {todo.text} </span> <button onClick={() => onDelete(todo.id)}>刪除</button> </div> ); });
4. 性能優(yōu)化最佳實(shí)踐
4.1 合理使用依賴項(xiàng)
function UserProfile({ userId, onUpdate }) { // ? 只在 userId 或 onUpdate 變化時(shí)更新 const handleUpdate = useCallback(() => { onUpdate(userId); }, [userId, onUpdate]); // ? 不必要的依賴項(xiàng) const handleClick = useCallback(() => { console.log('Clicked'); }, [userId]); // userId 不需要作為依賴項(xiàng) }
4.2 避免過(guò)度優(yōu)化
// ? 簡(jiǎn)單組件不需要使用 useCallback function SimpleButton({ onClick }) { return <button onClick={onClick}>Click me</button>; } // ? 復(fù)雜組件或頻繁重渲染的組件使用 useCallback const ComplexComponent = React.memo(function ComplexComponent({ onAction }) { // 復(fù)雜的渲染邏輯 return ( // ... ); });
5. useCallback 與其他 Hooks 配合
5.1 配合 useEffect 使用
function DataFetcher({ query }) { const [data, setData] = useState(null); // 記憶獲取數(shù)據(jù)的函數(shù) const fetchData = useCallback(async () => { const response = await fetch(`/api/search?q=${query}`); const result = await response.json(); setData(result); }, [query]); // 在 effect 中使用記憶的函數(shù) useEffect(() => { fetchData(); }, [fetchData]); // fetchData 作為依賴項(xiàng) return <div>{/* 渲染數(shù)據(jù) */}</div>; }
5.2 配合 useMemo 使用
function DataProcessor({ data, onProcess }) { // 記憶處理函數(shù) const processData = useCallback((item) => { // 復(fù)雜的數(shù)據(jù)處理邏輯 return someExpensiveOperation(item); }, []); // 使用記憶的函數(shù)處理數(shù)據(jù) const processedData = useMemo(() => { return data.map(processData); }, [data, processData]); return ( <div> {processedData.map(item => ( <ProcessedItem key={item.id} item={item} onProcess={onProcess} /> ))} </div> ); }
6. 注意事項(xiàng)
避免過(guò)度使用
- 只在性能確實(shí)受影響時(shí)使用
- 簡(jiǎn)單組件和回調(diào)不需要使用 useCallback
正確設(shè)置依賴項(xiàng)
- 包含所有回調(diào)中使用的變量
- 避免不必要的依賴項(xiàng)
配合 React.memo 使用
- 單獨(dú)使用 useCallback 可能無(wú)法帶來(lái)性能提升
- 需要配合 React.memo 等優(yōu)化手段
考慮使用場(chǎng)景
- 頻繁重渲染的組件
- 復(fù)雜的計(jì)算或操作
- 傳遞給多個(gè)子組件的回調(diào)
通過(guò)合理使用 useCallback 和 React.memo,我們可以有效優(yōu)化 React 應(yīng)用的性能。但要記住,過(guò)度優(yōu)化可能會(huì)適得其反,應(yīng)該在實(shí)際需要時(shí)才進(jìn)行優(yōu)化。
到此這篇關(guān)于React useCallback使用方法詳解的文章就介紹到這了,更多相關(guān)React useCallback內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
react+antd實(shí)現(xiàn)動(dòng)態(tài)編輯表格數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了react+antd實(shí)現(xiàn)動(dòng)態(tài)編輯表格數(shù)據(jù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08React路由的history對(duì)象的插件history的使用解讀
這篇文章主要介紹了React路由的history對(duì)象的插件history的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10React antd tabs切換造成子組件重復(fù)刷新
這篇文章主要介紹了React antd tabs切換造成子組件重復(fù)刷新,需要的朋友可以參考下2021-04-04React項(xiàng)目經(jīng)驗(yàn)總結(jié)及遇到的坑
這篇文章主要介紹了React項(xiàng)目經(jīng)驗(yàn)總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07在react中對(duì)less實(shí)現(xiàn)scoped配置方式
這篇文章主要介紹了在react中對(duì)less實(shí)現(xiàn)scoped配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11React實(shí)現(xiàn)動(dòng)效彈窗組件
最近在使用react開(kāi)發(fā)項(xiàng)目,遇到這樣一個(gè)需求實(shí)現(xiàn)一個(gè)帶有動(dòng)效的 React 彈窗組件,如果不考慮動(dòng)效,很容易實(shí)現(xiàn),接下來(lái)小編通過(guò)本文給大家介紹React實(shí)現(xiàn)動(dòng)效彈窗組件的實(shí)現(xiàn)代碼,一起看看吧2021-06-06React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容
這篇文章主要介紹了React-Native之TextInput組件的設(shè)置以及如何獲取輸入框的內(nèi)容問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05