useState 狀態(tài)管理
useState
是 React 中的一個基礎(chǔ) Hook,允許你在不使用 class 組件的情況下管理組件狀態(tài)。
參數(shù)
初始值
你可以直接傳遞狀態(tài)的初始值給 useState
:
const [name, setName] = useState("John");
使用函數(shù)設(shè)置初始值
當初始化狀態(tài)代價較大時,你可以傳遞一個函數(shù):
const [state, setState] = useState(() => {
const initialState = calculateInitialState(); // 一些復(fù)雜的操作
return initialState;
});
返回值
useState
返回一個數(shù)組,其中包括當前狀態(tài)值和一個更新狀態(tài)的函數(shù)。
示例
基礎(chǔ)計數(shù)器示例
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
</div>
);
}
使用對象作為狀態(tài)
function UserProfile() {
const [profile, setProfile] = useState({ name: "John", age: 30 });
const updateName = name => {
setProfile(prevProfile => ({ ...prevProfile, name }));
};
return (
<div>
<p>Name: {profile.name}</p>
<p>Age: {profile.age}</p>
<button onClick={() => updateName("Doe")}>Update Name</button>
</div>
);
}
使用多個 useState
你可以在一個組件中使用多個 useState
來管理不同的狀態(tài)片段:
function MultiCounter() {
const [count1, setCount1] = useState(0);
const [count2, setCount2] = useState(0);
return (
<div>
<p>Counter 1: {count1}</p>
<button onClick={() => setCount1(count1 + 1)}>Increment Counter 1</button>
<p>Counter 2: {count2}</p>
<button onClick={() => setCount2(count2 + 1)}>Increment Counter 2</button>
</div>
);
}
注意事項
-
調(diào)用位置:在 React 函數(shù)的最頂層調(diào)用
useState
。 -
依賴之前的狀態(tài)更新:使用函數(shù)式更新,如:
setCount(prevCount => prevCount + 1)
。 -
不會自動合并對象:手動合并對象,如:
setState(prevState => ({ ...prevState, key: 'value' }))
。
useEffect 副作用操作
React 函數(shù)組件的副作用利器
什么是副作用?
在編程中,副作用是指代碼對外部世界產(chǎn)生的影響。比如說,你想在用戶點擊按鈕后改變網(wǎng)頁的標題。這個改變就是一個副作用。
為什么需要 useEffect
?
React 組件通常用于渲染 UI,但有時你還需要執(zhí)行一些額外的操作,如網(wǎng)絡(luò)請求、操作 DOM 或訂閱事件等。這些操作就是所謂的副作用,而 useEffect
就是用來處理這些副作用的工具。
如何使用 useEffect
?
useEffect
的使用很簡單。它接受兩個參數(shù):一個副作用函數(shù)和一個依賴數(shù)組。
- 副作用函數(shù):放置你想執(zhí)行的副作用代碼。
- 依賴數(shù)組:決定副作用何時執(zhí)行。數(shù)組中的值發(fā)生變化時,副作用會重新執(zhí)行。
例子
改變網(wǎng)頁標題
假設(shè)你想在組件掛載后改變網(wǎng)頁標題。你可以這樣做:
useEffect(() => {
document.title = "新的標題";
}, []); // 空數(shù)組表示只在組件掛載后執(zhí)行一次
獲取用戶數(shù)據(jù)
如果你想根據(jù)用戶 ID 獲取用戶數(shù)據(jù),你可以這樣做:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
fetch(`/api/user/${userId}`)
.then(response => response.json())
.then(setUser);
}, [userId]); // 當 userId 改變時,重新獲取數(shù)據(jù)
}
注意事項
-
清理副作用:如果你的副作用涉及需要清理的操作(例如取消訂閱事件),你可以在副作用函數(shù)中返回一個清理函數(shù)。
useEffect(() => { const timerId = setInterval(() => { console.log("Tick"); }, 1000); return () => { clearInterval(timerId); // 清理定時器 }; }, []);
-
避免無限循環(huán):不小心使用
useEffect
可能導(dǎo)致無限循環(huán)。務(wù)必注意你的依賴數(shù)組,確保副作用按預(yù)期執(zhí)行。
useMemo 記憶計算值
useMemo
鉤子用于在組件渲染期間記住復(fù)雜計算的結(jié)果。這可以提高性能,尤其是當有大量計算和重新計算時。
為什么使用 useMemo
?
當組件重新渲染時,可能會涉及到一些復(fù)雜的計算。如果這些計算的依賴項沒有改變,那么重新進行這些計算就是浪費。useMemo
允許你記住這些計算的結(jié)果,只有當依賴項改變時才重新計算。
如何使用 useMemo
?
useMemo
接受兩個參數(shù):一個函數(shù)和一個依賴項數(shù)組。它會返回該函數(shù)的返回值。
- 函數(shù):包含要記住的計算。
- 依賴項數(shù)組:當數(shù)組中的任何值發(fā)生變化時,函數(shù)將被重新執(zhí)行。
TypeScript 示例代碼
下面的示例展示了如何使用 useMemo
來過濾大于 10 的數(shù)字。只有當數(shù)字數(shù)組改變時,才會重新計算過濾的結(jié)果。
import React, { useMemo, useState } from "react";
interface Props {
items: number[];
}
const ExpensiveComponent: React.FC<Props> = ({ items }) => {
const filteredItems = useMemo(() => items.filter(item => item > 10), [items]);
return (
<div>
<h3>Filtered Items (Greater than 10):</h3>
{filteredItems.map(item => (
<div key={item}>{item}</div>
))}
</div>
);
};
const App: React.FC = () => {
const [items, setItems] = useState<number[]>([5, 12, 8, 20, 33]);
const addItem = () => setItems([...items, Math.floor(Math.random() * 40)]);
return (
<div>
<button onClick={addItem}>Add Item</button>
<h3>All Items:</h3>
{items.map(item => (
<div key={item}>{item}</div>
))}
<ExpensiveComponent items={items} />
</div>
);
};
export default App;
注意事項
- 不要將
useMemo
作為性能優(yōu)化的首要工具。除非你確實遇到性能問題,并且確定了useMemo
可以解決問題,否則不要過早優(yōu)化。 - 不應(yīng)該在
useMemo
的函數(shù)內(nèi)部執(zhí)行有副作用的操作。使用useEffect
處理副作用。
總結(jié)
useMemo
是一個非常強大的工具,可以提高組件的性能,尤其是在復(fù)雜的計算和頻繁的重新渲染中。但是,也要謹慎使用它,確保只在確實需要時使用它,并始終確保你的代碼的正確性和可維護性。
useCallback :記憶化函數(shù)
什么是 useCallback
?
useCallback
用于在組件的連續(xù)渲染之間記憶化(或緩存)函數(shù)。它幫助避免因父組件重新渲染而導(dǎo)致的不必要的子組件渲染。
如何使用?
useCallback
接受兩個參數(shù):一個函數(shù)和一個依賴數(shù)組。只有當依賴數(shù)組中的值發(fā)生變化時,函數(shù)才會被重新創(chuàng)建。
示例
讓我們看一個完整的示例,展示如何使用 useCallback
來優(yōu)化組件的渲染。
import React, { useState, useCallback } from "react";
const ChildComponent = React.memo(({ onClick }) => {
console.log("ChildComponent re-rendered!");
return <button onClick={onClick}>Click Me!</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// 使用 useCallback 記憶化 handleClick 函數(shù)
const handleClick = useCallback(() => {
console.log("Button was clicked");
}, []); // 空依賴數(shù)組,表示該函數(shù)不會重新創(chuàng)建
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increment Counter</button>
{/* 傳遞記憶化的 handleClick 到 ChildComponent */}
<ChildComponent onClick={handleClick} />
</div>
);
}
export default ParentComponent;
在上面的示例中,ChildComponent
接受一個 onClick
屬性,并且使用 React.memo
進行了優(yōu)化,所以只有當 onClick
屬性發(fā)生變化時才會重新渲染。由于 handleClick
使用 useCallback
被記憶化了,因此即使父組件重新渲染,handleClick
函數(shù)也不會更改,從而避免了子組件的不必要渲染。
注意事項
-
過度優(yōu)化的風(fēng)險:不是所有的函數(shù)都需要使用
useCallback
。如果一個函數(shù)沒有傳遞給子組件或其他記憶化操作,使用useCallback
可能會帶來更多的復(fù)雜性和性能開銷。 - 依賴項的準確性:確保依賴數(shù)組中的所有值都是必需的,并且當它們發(fā)生變化時,你確實希望函數(shù)被重新創(chuàng)建。
總結(jié)
useCallback
是一個強大的優(yōu)化工具,可以在適當?shù)那闆r下提高 React 組件的性能。通過使用此 Hook,你可以控制函數(shù)的重新創(chuàng)建,從而避免因父組件重新渲染而導(dǎo)致的不必要的子組件渲染。
該示例展示了如何在實際組件中使用 useCallback
進行優(yōu)化,希望這有助于你更深入地理解這個 Hook 的用途和工作方式。如果你有任何問題或需要進一步的解釋,請隨時提問!
useCallback 和 useMemo 區(qū)別
-
用途:
useCallback
用于記憶化函數(shù),而useMemo
用于記憶化計算值。 -
返回值:
useCallback
返回一個記憶化的函數(shù);useMemo
返回一個記憶化的計算結(jié)果。 - 最佳實踐
- 當你需要傳遞給子組件的函數(shù)并希望避免不必要的子組件渲染時,使用
useCallback
。 - 當你有昂貴的計算,并希望在依賴項未更改時避免重新計算時,使用
useMemo
- 當你需要傳遞給子組件的函數(shù)并希望避免不必要的子組件渲染時,使用
useRef 訪問 Ref
用于訪問和操作 DOM 元素及保持可變的引用值
什么是 useRef
?
useRef
Hook 用于訪問和操作 DOM 元素,并在組件的整個生命周期中保持不變的引用。除了用于直接操作 DOM,useRef
還可用于在組件的不同渲染之間保留可變的引用值,而不觸發(fā)重新渲染。
如何使用 useRef
?
要使用 useRef
,你首先需要調(diào)用它,并將初始值作為參數(shù)傳遞(如果有)。這將返回一個 ref
對象,該對象具有一個名為 current
的屬性,該屬性將引用傳遞給 useRef
的初始值或 DOM 元素。
使用 useRef
訪問 DOM 元素
你可以使用 useRef
直接訪問和操作 DOM 元素。下面是一個示例,顯示了如何使用 useRef
控制輸入元素的焦點。
import React, { useRef } from "react";
function TextInput() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus(); // 使用 ref 對象的 current 屬性訪問輸入元素
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleFocus}>Focus the input</button>
</div>
);
}
在上述示例中,我們創(chuàng)建了一個 ref
對象并將其分配給輸入元素。然后,我們可以使用該引用在按鈕點擊時將焦點設(shè)置到輸入元素上。
使用 useRef
保留可變的引用值
除了用于訪問 DOM 元素外,useRef
還可以用于在組件的連續(xù)渲染之間保留可變的引用值,而不觸發(fā)重新渲染。
import React, { useRef, useEffect } from "react";
function Timer() {
const countRef = useRef(0);
useEffect(() => {
const intervalId = setInterval(() => {
countRef.current += 1; // 更新 ref 的 current 值
console.log("Timer:", countRef.current);
}, 1000);
return () => clearInterval(intervalId); // 清除計時器
}, []); // 空依賴數(shù)組表示該效果只在掛載和卸載時運行
return <div>Check the console to see the timer count!</div>;
}
在這個示例中,我們使用 useRef
來保持計時器的計數(shù)值。因為 ref
對象的更改不會觸發(fā)組件的重新渲染,所以它是一個非常有用的工具,用于在重新渲染之間保留值。
總結(jié)
useRef
是一個多功能的 React Hook,可以用于多種用途:
-
訪問和操作 DOM 元素:使用 ref 對象的
current
屬性直接訪問和操作 DOM 元素。 - 保留可變引用值:在組件的不同渲染之間保留值,而不觸發(fā)重新渲染。
自定義組件使用 Ref
在 React 中,你可以使用引用(ref)與自定義組件進行交互,訪問組件的實例。這是一個非常有用的特性,可以用于獲取組件內(nèi)部的信息,調(diào)用組件內(nèi)部的方法,或者與組件進行更復(fù)雜的交互。
使用 forwardRef 與自定義組件
對于自定義組件,你通常需要使用 forwardRef
來轉(zhuǎn)發(fā) ref。下面是一個簡單的示例,顯示如何使用 forwardRef
創(chuàng)建自定義組件,并從父組件中訪問該組件的 DOM 元素:
import React, { useRef, forwardRef } from "react";
const CustomComponent = forwardRef((props, ref) => {
return <div ref={ref}>Custom Component</div>;
});
function App() {
const customComponentRef = useRef(null);
const handleClick = () => {
customComponentRef.current.style.backgroundColor = "lightblue";
};
return (
<div>
<CustomComponent ref={customComponentRef} />
<button onClick={handleClick}>Change Background Color</button>
</div>
);
}
export default App;
使用 useImperativeHandle 暴露自定義實例屬性
有時,你可能希望自定義組件能夠暴露特定的實例方法或?qū)傩越o父組件。在這種情況下,你可以使用 useImperativeHandle
進行更精細的控制。下面的示例顯示了如何使用 useImperativeHandle
暴露組件的特定方法:
import React, { useRef, forwardRef, useImperativeHandle } from "react";
const CustomComponent = forwardRef((props, ref) => {
useImperativeHandle(ref, () => ({
sayHello: () => {
alert("Hello from CustomComponent!");
},
}));
return <div>Custom Component</div>;
});
function App() {
const customComponentRef = useRef(null);
const handleClick = () => {
customComponentRef.current.sayHello(); // 調(diào)用自定義組件的方法
};
return (
<div>
<CustomComponent ref={customComponentRef} />
<button onClick={handleClick}>Say Hello</button>
</div>
);
}
export default App;
總結(jié)
與自定義組件一起使用 ref 可以實現(xiàn)許多強大的功能,從訪問組件內(nèi)部的 DOM 元素,到調(diào)用組件的特定實例方法。通過使用 forwardRef
和 useImperativeHandle
,你可以更靈活地控制自定義組件的行為,并與之進行更復(fù)雜的交互。
請注意,直接操作 DOM 和組件實例通常應(yīng)該作為最后的手段,因為它可能會導(dǎo)致代碼難以理解和維護。在可能的情況下,盡量使用正常的 React 數(shù)據(jù)流來管理組件的狀態(tài)和行為。
useContext 訪問上下文
useContext
鉤子是 React 中一個非常強大的工具,用于在組件樹中跨層級共享狀態(tài)。它消除了通過多層組件手動傳遞 props 的需要,使得狀態(tài)管理變得更加清晰和高效。
為什么使用 useContext
?
在復(fù)雜的 React 應(yīng)用程序中,狀態(tài)需要在多個組件之間共享。傳統(tǒng)方法是將狀態(tài)作為 props 從頂層組件一層一層傳遞下去,但這會導(dǎo)致代碼混亂且難以維護。useContext
允許我們跨越組件層級直接共享狀態(tài),無需手動傳遞。
如何使用 useContext
?
-
創(chuàng)建上下文: 使用 React 的
createContext
方法創(chuàng)建一個上下文。 -
提供上下文: 使用
Context.Provider
組件在組件樹中提供上下文的值。 -
消費上下文: 在任何子組件中使用
useContext
鉤子訪問上下文的值。
TypeScript 示例代碼
下面的示例展示了如何使用 useContext
來共享主題設(shè)置。
import React, { useContext, useState } from "react";
// 創(chuàng)建上下文
const ThemeContext = React.createContext<{ theme: string; toggleTheme: () => void } | undefined>(undefined);
const App: React.FC = () => {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
// 提供上下文
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<Header />
<MainContent />
</ThemeContext.Provider>
);
};
const Header: React.FC = () => {
// 消費上下文
const themeContext = useContext(ThemeContext);
if (!themeContext) {
return <h1>Error: Theme not found</h1>;
}
return (
<div>
<h1>{themeContext.theme} theme is active</h1>
<button onClick={themeContext.toggleTheme}>Toggle Theme</button>
</div>
);
};
const MainContent: React.FC = () => {
// ...
return <div>Main Content</div>;
};
export default App;
注意事項
- 確保在使用
useContext
之前已經(jīng)在組件樹中提供了上下文。 - 不要在渲染過程中更改上下文的值,這可能會導(dǎo)致組件的不必要重新渲染。
總結(jié)
useContext
鉤子是一個強大的工具,用于組件之間的跨層級狀態(tài)共享。通過消除手動傳遞 props 的需要,它可以使你的代碼更加清晰、簡潔,同時也提高了代碼的可維護性。
useReducer 復(fù)雜狀態(tài)邏輯
useReducer
是 React 中用于處理組件狀態(tài)邏輯的鉤子,尤其適用于更復(fù)雜或包括多個子值的狀態(tài)。它的工作原理類似于 Redux,但是更加精簡,不需要引入額外的庫。
為什么使用 useReducer
?
當狀態(tài)邏輯復(fù)雜或者下一狀態(tài)依賴于之前的狀態(tài)時,useReducer
非常有用。相較于 useState
,它提供了更可預(yù)測的狀態(tài)更新方式,并且更容易測試和維護。
如何使用 useReducer
?
useReducer
接收兩個參數(shù):一個 reducer 函數(shù)和初始狀態(tài),返回當前狀態(tài)和一個 dispatch 函數(shù)。
-
Reducer 函數(shù): 這個函數(shù)接收兩個參數(shù):當前的狀態(tài)和一個動作。根據(jù)動作類型,它返回一個新的狀態(tài)。
-
初始狀態(tài): 初始狀態(tài)是 reducer 的第一個參數(shù)的初始值。
-
Dispatch 函數(shù): 這個函數(shù)用于分派動作,觸發(fā) reducer 函數(shù),并更新狀態(tài)。
TypeScript 示例代碼
下面的示例演示了如何使用 useReducer
來管理一個計數(shù)器的狀態(tài)。
import React, { useReducer } from "react";
// 定義動作類型
type Action = { type: "increment" } | { type: "decrement" };
// 定義reducer函數(shù)
const counterReducer = (state: number, action: Action) => {
switch (action.type) {
case "increment":
return state + 1;
case "decrement":
return state - 1;
default:
return state;
}
};
const Counter: React.FC = () => {
// 使用useReducer
const [state, dispatch] = useReducer(counterReducer, 0);
return (
<div>
<h1>Count: {state}</h1>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
</div>
);
};
export default Counter;
注意事項
- Reducer 必須是一個純函數(shù),這意味著同樣的輸入必須產(chǎn)生相同的輸出,不應(yīng)有副作用。
- 盡量使 reducer 函數(shù)保持簡潔明了,可以通過拆分成多個子 reducer 來處理更復(fù)雜的狀態(tài)邏輯。
總結(jié)
useReducer
鉤子提供了一種更靈活、可維護的方式來處理復(fù)雜的狀態(tài)邏輯。通過結(jié)合純凈的 reducer 函數(shù)和動作分派,它為開發(fā)人員提供了對組件內(nèi)部狀態(tài)更細粒度的控制。對于需要管理復(fù)雜狀態(tài)或者希望與 Redux 保持一致的項目,useReducer
是一個極好的選擇。
useImperativeHandle 自定義 ref 暴露
useImperativeHandle
是一種特殊的鉤子,允許你在父組件中直接操作子組件中的某些實例方法。通常來說,在 React 中,組件應(yīng)該遵循數(shù)據(jù)自上而下的流動,并通過屬性和狀態(tài)進行通信。但有時,你可能需要在父組件中直接調(diào)用子組件的某些方法。這就是 useImperativeHandle
發(fā)揮作用的地方。
這個鉤子不常用,因為它打破了 React 的一些核心原則,但在某些特定場景下可能很有用。
參數(shù)
useImperativeHandle
接受三個參數(shù):
- ref: 父組件傳遞給子組件的 ref 對象。
- createHandle: 一個返回包含暴露給父組件的實例方法的對象的函數(shù)。
-
deps: 一個依賴數(shù)組,類似于
useEffect
或useMemo
等鉤子。如果提供了此參數(shù),則只有當依賴項更改時,才會更新實例方法。
示例
假設(shè)你有一個 TextInput
組件,并且你想暴露一個方法來清除輸入。文章來源:http://www.zghlxwxcb.cn/news/detail-637956.html
import React, { useImperativeHandle, forwardRef, useRef } from "react";
type TextInputHandles = {
clear: () => void;
};
const TextInput = forwardRef<TextInputHandles, {}>((props, ref) => {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
clear: () => {
if (inputRef.current) {
inputRef.current.value = "";
}
},
}));
return <input type="text" ref={inputRef} />;
});
const ParentComponent = () => {
const inputRef = useRef<TextInputHandles>(null);
const clearInput = () => {
if (inputRef.current) {
inputRef.current.clear();
}
};
return (
<div>
<TextInput ref={inputRef} />
<button onClick={clearInput}>Clear Input</button>
</div>
);
};
export default ParentComponent;
注意事項
-
謹慎使用: 盡量不要過度使用
useImperativeHandle
,因為它可能導(dǎo)致代碼更難理解和維護。 -
與
forwardRef
配合使用: 通常,你需要將子組件與forwardRef
一起使用,以將 ref 傳遞給子組件。
總結(jié)
雖然 useImperativeHandle
不是常用的鉤子,但在需要在父組件中直接操作子組件的特定場景下,它可能是必要的。通過允許你精確地控制父組件可以訪問的子組件方法,它提供了一種強大但易于濫用的工具。在使用它時要小心,并確保這確實是解決問題的最佳方式。文章來源地址http://www.zghlxwxcb.cn/news/detail-637956.html
到了這里,關(guān)于React Hooks 詳細使用介紹的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!