React基礎學習-Day04

React基礎學習-Day04

常見的鉤子函數及基礎使用方式

1.useState

useState 是 React 的一個 Hook,用于在函數組件中添加狀態。它返回一個狀態變量和一個更新該狀態的函數。與類組件的 this.statethis.setState 相對應,useState 讓函數組件也能擁有和管理狀態。

基本用法

以下是 useState 的基本用法示例:

import React, { useState } from 'react';const Counter = () => {// 聲明一個名為 "count" 的狀態變量,初始值為 0const [count, setCount] = useState(0);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);
};export default Counter;

在這個示例中,useState(0) 聲明了一個名為 count 的狀態變量,并將其初始值設為 0setCount 是更新 count 狀態的函數。當點擊按鈕時,調用 setCount(count + 1)count 增加 1,并重新渲染組件。

多個狀態

可以在同一個組件中使用多個 useState 聲明不同的狀態變量:

import React, { useState } from 'react';const UserProfile = () => {const [name, setName] = useState('Alice');const [age, setAge] = useState(25);return (<div><p>Name: {name}</p><p>Age: {age}</p><button onClick={() => setName('Bob')}>Change Name</button><button onClick={() => setAge(age + 1)}>Increase Age</button></div>);
};export default UserProfile;

在這個示例中,我們使用了兩個 useState Hook,一個用于管理 name 狀態,另一個用于管理 age 狀態。每個狀態都有自己獨立的更新函數。

初始化狀態

useState 可以接受一個函數作為初始狀態,這個函數在初始渲染時會被調用,用于計算初始狀態。這對于需要復雜計算才能得到初始狀態的情況非常有用。

import React, { useState } from 'react';const ExpensiveComponent = () => {const [value, setValue] = useState(() => {// 模擬一個耗時的計算const initialValue = computeExpensiveValue();return initialValue;});return (<div><p>Value: {value}</p><button onClick={() => setValue(value + 1)}>Increment</button></div>);
};const computeExpensiveValue = () => {// 這里模擬一個復雜的計算console.log('Computing expensive value...');return 42;
};export default ExpensiveComponent;

在這個示例中,useState 接受一個函數,該函數只在初始渲染時被調用一次,用于計算初始狀態值。

注意事項

  • 每次調用 setState 更新狀態時,React 會重新渲染組件。
  • 更新狀態是異步的,因此在調用 setState 后立即讀取狀態可能不會得到最新的值。
  • 可以通過函數式更新來確保狀態更新基于最新的狀態值:
const [count, setCount] = useState(0);const increment = () => {setCount(prevCount => prevCount + 1);
};

在這個示例中,setCount 接受一個函數,該函數的參數是之前的狀態值,返回的新狀態值將基于之前的狀態值進行更新。

完整示例

以下是一個使用多個狀態和函數式更新的完整示例:

import React, { useState } from 'react';const App = () => {const [count, setCount] = useState(0);const [text, setText] = useState('Hello');const increment = () => {setCount(prevCount => prevCount + 1);};const changeText = () => {setText(prevText => prevText === 'Hello' ? 'World' : 'Hello');};return (<div><p>Count: {count}</p><p>Text: {text}</p><button onClick={increment}>Increment Count</button><button onClick={changeText}>Change Text</button></div>);
};export default App;

在這個示例中,我們使用了 useState 來管理兩個獨立的狀態變量 counttext,并使用函數式更新確保狀態更新基于最新的狀態值。這有助于避免狀態更新的競爭條件。

ps:useState的第一個參數用函數可以使用異步函數嗎

useState 的第一個參數是初始狀態,它可以是一個值或者是一個函數,但這個函數必須是同步的。React 不支持在 useState 的初始狀態函數中使用異步函數。useState 的初始狀態函數只會在組件的初始渲染時被調用一次,并且它是同步的。

如果你需要在組件初始化時從異步操作中獲取數據并設置狀態,你可以在 useEffect Hook 中執行異步操作,并在操作完成后更新狀態。

以下是一個如何使用 useEffectuseState 來處理異步操作的示例:

import React, { useState, useEffect } from 'react';const FetchDataComponent = () => {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {const fetchData = async () => {try {const response = await fetch('https://api.example.com/data');if (!response.ok) {throw new Error('Network response was not ok');}const result = await response.json();setData(result);} catch (error) {setError(error);} finally {setLoading(false);}};fetchData();}, []); // 空數組意味著這個 effect 只會在組件掛載和卸載時執行一次if (loading) {return <div>Loading...</div>;}if (error) {return <div>Error: {error.message}</div>;}return (<div><h1>Fetched Data:</h1><pre>{JSON.stringify(data, null, 2)}</pre></div>);
};export default FetchDataComponent;

在這個示例中:

  1. useState 用于管理組件的狀態,包括 dataloadingerror
  2. useEffect 用于執行異步數據獲取操作。當組件首次掛載時,這個 effect 會運行,觸發 fetchData 異步函數。
  3. fetchData 異步函數使用 fetch 從 API 獲取數據。如果成功,數據將被存儲在 data 狀態中,并且 loading 狀態被設置為 false。如果出現錯誤,error 狀態將被設置,并且 loading 狀態同樣被設置為 false

這樣,雖然不能在 useState 的初始狀態函數中使用異步函數,但可以通過 useEffect 來實現異步數據獲取和狀態管理。

2.useEffect

useEffect 是 React 的一個 Hook,用于在函數組件中執行副作用操作。副作用操作包括數據獲取、訂閱、手動 DOM 操作以及在組件更新或卸載時執行清理任務。useEffect 可以看作是 componentDidMountcomponentDidUpdatecomponentWillUnmount 的組合。

基本用法

以下是 useEffect 的基本用法示例:

import React, { useState, useEffect } from 'react';const ExampleComponent = () => {const [count, setCount] = useState(0);// useEffect 中的函數會在組件渲染后執行useEffect(() => {document.title = `Count: ${count}`;}, [count]); // 依賴項數組return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment</button></div>);
};export default ExampleComponent;

在這個示例中,useEffect 中的函數會在組件每次渲染后執行。依賴項數組 [count] 指定了只有當 count 狀態發生變化時,useEffect 才會重新運行。

依賴項數組

useEffect 的第二個參數是依賴項數組,指定了哪些狀態或屬性的變化會觸發 useEffect 的重新運行:

  • 空數組 []:僅在組件掛載和卸載時運行一次。
  • 未指定依賴項數組:在每次組件渲染后都會運行。
  • 特定依賴項:當依賴項發生變化時才會運行。

清理副作用

如果 useEffect 返回一個函數,這個函數會在組件卸載時或在下一次執行副作用前被調用,用于清理副作用。例如,清理訂閱或取消定時器:

import React, { useState, useEffect } from 'react';const TimerComponent = () => {const [seconds, setSeconds] = useState(0);useEffect(() => {const interval = setInterval(() => {setSeconds(prevSeconds => prevSeconds + 1);}, 1000);// 返回一個清理函數,在組件卸載時清除定時器return () => clearInterval(interval);}, []); // 空數組意味著這個 effect 只會在組件掛載和卸載時執行一次return (<div><p>Seconds: {seconds}</p></div>);
};export default TimerComponent;

在這個示例中,useEffect 設置了一個定時器,每秒更新一次 seconds 狀態。在 useEffect 中返回的清理函數會在組件卸載時被調用,以清除定時器。

數據獲取示例

以下是一個使用 useEffect 執行數據獲取操作的示例:

import React, { useState, useEffect } from 'react';const FetchDataComponent = () => {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {const fetchData = async () => {try {const response = await fetch('https://api.example.com/data');if (!response.ok) {throw new Error('Network response was not ok');}const result = await response.json();setData(result);} catch (error) {setError(error);} finally {setLoading(false);}};fetchData();}, []); // 空數組意味著這個 effect 只會在組件掛載和卸載時執行一次if (loading) {return <div>Loading...</div>;}if (error) {return <div>Error: {error.message}</div>;}return (<div><h1>Fetched Data:</h1><pre>{JSON.stringify(data, null, 2)}</pre></div>);
};export default FetchDataComponent;

在這個示例中,useEffect 用于執行異步數據獲取操作。組件掛載時,fetchData 函數會運行,并在數據獲取成功后更新 data 狀態。如果出現錯誤,error 狀態會被更新。

總結

useEffect 是一個功能強大的 Hook,用于在函數組件中管理副作用操作。通過合理使用依賴項數組和清理函數,useEffect 可以幫助我們在組件的生命周期中執行和管理各種副作用。

3.useContext

useContext 是 React 的一個 Hook,用于在函數組件中讀取和使用 React 上下文(Context)。上下文允許您在組件樹中傳遞數據,而不必手動逐層傳遞 props。

基本用法

以下是 useContext 的基本用法示例:

假設我們有一個上下文對象 ThemeContext

import React, { createContext, useContext, useState } from 'react';// 創建一個上下文對象
const ThemeContext = createContext();// 上下文的提供者組件
const ThemeProvider = ({ children }) => {const [theme, setTheme] = useState('light');const toggleTheme = () => {setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));};return (<ThemeContext.Provider value={{ theme, toggleTheme }}>{children}</ThemeContext.Provider>);
};// 使用上下文的消費者組件
const ThemeConsumerComponent = () => {const { theme, toggleTheme } = useContext(ThemeContext);return (<div><p>Current Theme: {theme}</p><button onClick={toggleTheme}>Toggle Theme</button></div>);
};// 在組件樹中使用 ThemeProvider 包裹所有需要訪問 theme 上下文的組件
const App = () => {return (<ThemeProvider><ThemeConsumerComponent /></ThemeProvider>);
};export default App;

在這個示例中:

  1. ThemeContext 是一個上下文對象,通過 createContext() 創建。
  2. ThemeProvider 是一個提供者組件,使用 ThemeContext.Provider 包裝其子組件,并通過 value 屬性傳遞 themetoggleTheme 函數。
  3. ThemeConsumerComponent 是一個消費者組件,使用 useContext(ThemeContext) 來訂閱 ThemeContext 上下文。它從上下文中讀取 themetoggleTheme,并在按鈕點擊時切換主題。

注意事項

  • 使用 createContext() 創建上下文對象。
  • 使用 Provider 組件包裹子組件,并通過 value 屬性傳遞數據。
  • 在需要訪問上下文數據的組件中使用 useContext(ThemeContext)

多個上下文

您可以在應用程序中使用多個上下文。每個上下文對象都應該有自己的提供者和消費者組件。React 會確保正確的數據傳遞和更新。

總結

useContext Hook 提供了一種輕松地在函數組件中使用 React 上下文的方式。它使得組件可以更簡潔地訂閱和使用跨組件樹的數據。通過 useContext,您可以避免 props drilling,提高組件的可重用性和可維護性。

4.useReducer

一、介紹useReducer的重要性和優勢

useReducer是 React 中的一個 Hook,用于管理應用狀態。它提供了一種更簡潔、更易于理解的方式來處理復雜的狀態邏輯。

重要性:

狀態管理:在 React 應用中,狀態管理是至關重要的。useReducer允許開發者以更清晰和集中的方式管理應用的狀態。
復雜狀態邏輯:對于涉及多個狀態變量和復雜的更新邏輯的場景,使用useReducer可以更好地組織和維護代碼。
可預測的狀態更新:useReducer使用函數來更新狀態,這使得狀態更新更加可預測和易于理解。
更好的代碼可讀性:通過使用useReducer,可以將狀態更新邏輯拆分為獨立的函數,提高代碼的可讀性和可維護性。

img

優勢:

簡化代碼:相比使用多個 useState 鉤子來管理復雜的狀態,useReducer 可以減少代碼的冗余。
更好的性能:useReducer在某些情況下可以提供更好的性能,尤其是在處理大量狀態更新時。
狀態的合并:useReducer支持合并多個更新操作,從而減少不必要的重新渲染。
清晰的狀態更新邏輯:使用useReducer可以將狀態更新邏輯放在一個單獨的函數中,使其更加清晰和易于理解。

img

總之,useReducer對于處理復雜的狀態邏輯和更好地組織狀態更新非常有用。它提供了一種更簡潔、可預測和可讀性更高的方式來管理應用狀態。在需要處理復雜狀態的情況下,推薦使用useReducer。

二、useReducer的基本概念

解釋useReducer的定義和作用
useReducer是React Hooks中的一個函數,它用于在React應用程序中實現狀態管理。useReducer函數接收兩個參數:一個是reducer函數,另一個是初始狀態。reducer函數接收兩個參數:一個是當前狀態,另一個是action對象。action對象通常包含一個type屬性,表示要進行的操作,以及可能的其他屬性。

useReducer函數返回一個數組,數組的第一個元素是當前狀態,第二個元素是一個函數,該函數用于更新狀態。當組件需要更新狀態時,它將調用該函數,并將新狀態作為參數傳遞給它。該函數將使用reducer函數來計算新狀態,并將其返回給組件。

下面是一個簡單的示例,演示如何使用useReducer來管理計數器狀態:

import React, { useReducer } from 'react';function reducer(state, action) {switch (action.type) {case 'increment':return state + 1;case 'decrement':return state - 1;default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, 0);const handleIncrement = () => {dispatch({ type: 'increment' });};const handleDecrement = () => {dispatch({ type: 'decrement' });};return (<div><h1>{state}</h1><button onClick={handleIncrement}>+</button><button onClick={handleDecrement}>-</button></div>);
}

在這個示例中,我們定義了一個reducer函數reducer,它接收兩個參數:當前狀態state和action對象action。action對象包含一個type屬性,表示要進行的操作。然后,我們使用useReducer函數將reducer函數和初始狀態傳遞給組件。useReducer函數返回一個數組,數組的第一個元素是當前狀態,第二個元素是一個函數,用于更新狀態。

我們定義了兩個函數handleIncrement和handleDecrement,分別用于處理加1和減1操作。然后,我們將這些函數綁定到按鈕的onClick事件上,以便在按鈕被點擊時調用它們。

最后,我們將當前狀態顯示在頁面上,以便用戶可以看到計數器的值在不斷變化。當用戶點擊按鈕時,我們將調用dispatch函數,并將相應的action對象傳遞給它。dispatch函數將調用reducer函數來計算新狀態,并將新狀態返回給組件。

總之,useReducer函數在React應用程序中提供了一個簡單、高效的狀態管理解決方案,可以用于管理復雜的應用程序狀態。

與其他狀態管理方法進行比較
以下是使用表格總結的useReducer與其他狀態管理方法的比較:

方法 描述 優點 缺點
useState React內置的狀態管理方法,用于管理簡單的狀態。 簡單易用 無法處理復雜的業務邏輯
useEffect 用于在函數組件中添加副作用,如數據獲取、訂閱等。 靈活性高 需要在組件內手動處理副作用
useContext 用于在不同組件之間共享狀態,而不需要顯式地傳遞狀態。 共享狀態簡單易用 無法處理副作用
useReducer 用于管理復雜的狀態,如復雜的業務邏輯、表格數據等。 靈活性高,易于處理復雜的業務邏輯 需要手動編寫reducer函數
從表格中可以看出,useReducer方法在處理復雜的狀態上具有優勢,因為它可以方便地使用reducer函數來處理復雜的業務邏輯。同時,它也可以處理副作用,如數據獲取、訂閱等。但是,它需要手動編寫reducer函數,這可能會增加一些復雜性。而useState和useEffect方法則更適合處理簡單的狀態和管理副作用。useContext方法則更適合在不同組件之間共享狀態。

總之,選擇哪種狀態管理方法取決于具體的需求和組件的結構。在實際開發中,可以根據項目的規模和復雜度來選擇合適的狀態管理方法。

三、useReducer的使用示例

解釋useReducer的參數和返回值
useReducer 是一個 React 狀態管理方法,它的參數和返回值如下:

參數:

reducer 函數:這個函數接收兩個參數,分別是當前狀態(state)和一個 action。該函數的作用是處理傳入的狀態,并返回一個新的狀態。
initialState:狀態的初始值。
返回值:

新的 state:由 reducer 函數處理后返回的新狀態。
dispatch 函數:用于發送一個對象(action)給 reducer 函數,以更新狀態,并觸發重新渲染。
展示如何更新狀態和觸發重新渲染
以下是使用 useReducer 更新狀態和觸發重新渲染的示例:

import React, { useReducer } from 'react';const initialState = {count: 0
};function reducer(state, action) {switch (action.type) {case 'increment':return {...state,count: state.count + 1};case 'decrement':return {...state,count: state.count - 1};default:return state;}
}function MyComponent() {// 使用 useReducer 來創建狀態和更新函數const [state, dispatch] = useReducer(reducer, initialState);const handleIncrement = () => {// 發送一個 increment 類型的 action 來更新狀態dispatch({ type: 'increment' });};const handleDecrement = () => {// 發送一個 decrement 類型的 action 來更新狀態dispatch({ type: 'decrement' });};return (<div><h1>{state.count}</h1><button onClick={handleIncrement}>+</button><button onClick={handleDecrement}>-</button></div>);
}export default MyComponent;

在上述示例中,使用 useReducer 創建了一個狀態 count,初始值為 0。定義了一個 reducer 函數來處理狀態的更新。reducer 函數根據不同的 action.type 來執行相應的狀態更新操作。

通過調用 dispatch 函數并傳入一個對象作為 action,可以更新狀態。在示例中,點擊 “+” 按鈕會發送一個 increment 類型的 action,點擊 “-” 按鈕會發送一個 decrement 類型的 action。

當狀態更新后,組件會重新渲染,顯示最新的狀態值。

5.useRef

useRef 是 React 的一個 Hook,用于在函數組件中創建可變的引用對象。與 useState 不同,useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化為傳入的參數(initialValue)。返回的 ref 對象在組件的整個生命周期中保持不變,不會因重新渲染而重新創建。

主要用途

  1. 獲取 DOM 元素的引用

    import React, { useRef, useEffect } from 'react';const TextInputComponent = () => {const inputRef = useRef(null);useEffect(() => {// 在組件加載后聚焦到輸入框inputRef.current.focus();}, []);return (<div><input type="text" ref={inputRef} /><button onClick={() => inputRef.current.focus()}>Focus Input</button></div>);
    };export default TextInputComponent;
    

    在這個示例中,useRef 創建了一個 inputRef 對象,并將其賦值給 <input> 元素的 ref 屬性。通過 inputRef.current 可以訪問到真實的 DOM 元素,比如調用 .focus() 方法來聚焦輸入框。

  2. 保存任意可變值

    import React, { useRef } from 'react';const CounterComponent = () => {const countRef = useRef(0);const increment = () => {countRef.current++;console.log('Current count:', countRef.current);};return (<div><p>Count: {countRef.current}</p><button onClick={increment}>Increment</button></div>);
    };export default CounterComponent;
    

    在這個示例中,useRef 創建了一個 countRef 對象,并初始化為 0。在 increment 函數中,通過修改 countRef.current 來更新計數器的值,而不需要觸發組件的重新渲染。

  3. 保存上一個 props 或 state

    import React, { useRef, useEffect } from 'react';const PreviousValueComponent = ({ value }) => {const prevValueRef = useRef();useEffect(() => {prevValueRef.current = value;});const prevValue = prevValueRef.current;return (<div><p>Current Value: {value}</p><p>Previous Value: {prevValue !== undefined ? prevValue : 'N/A'}</p></div>);
    };export default PreviousValueComponent;
    

    在這個示例中,通過 useRef 創建了 prevValueRef 對象,用于保存 value 的上一個值。通過在 useEffect 中更新 prevValueRef.current,可以在組件的重新渲染中獲取到上一個 value 的值。

注意事項

  • useRef 創建的 ref 對象在組件的整個生命周期中保持不變,不會因重新渲染而重新創建。
  • 修改 useRef 創建的 ref 對象的 .current 屬性不會觸發組件的重新渲染。
  • 可以通過 useRef 來保存和訪問 DOM 元素的引用、保存任意可變值以及保存上一個 props 或 state 的值。

6.useCallback

useCallback 是 React 的一個 Hook,它返回一個記憶化的回調函數,用于優化函數組件的性能,防止不必要的重新渲染和重新創建函數。在依賴項沒有發生變化時,useCallback 返回的函數引用保持不變,從而避免子組件因為父組件的函數變化而重新渲染。

基本用法

以下是 useCallback 的基本用法示例:

import React, { useState, useCallback } from 'react';const ChildComponent = React.memo(({ onClick }) => {console.log('Rendering ChildComponent');return <button onClick={onClick}>Click me</button>;
});const ParentComponent = () => {const [count, setCount] = useState(0);const handleClick = useCallback(() => {console.log('Button clicked');}, []);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment Count</button><ChildComponent onClick={handleClick} /></div>);
};export default ParentComponent;

在這個示例中,handleClick 函數使用了 useCallback,并且依賴項數組為空([]),這意味著 handleClick 函數在組件的整個生命周期中只會創建一次。這樣,ChildComponent 組件不會因為 ParentComponent 重新渲染而重新渲染,因為 onClick 屬性的引用沒有變化。

依賴項數組

useCallback 的第二個參數是依賴項數組,只有當依賴項數組中的某個依賴項發生變化時,才會重新創建回調函數。例如:

import React, { useState, useCallback } from 'react';const ChildComponent = React.memo(({ onClick }) => {console.log('Rendering ChildComponent');return <button onClick={onClick}>Click me</button>;
});const ParentComponent = () => {const [count, setCount] = useState(0);const [text, setText] = useState('Hello');const handleClick = useCallback(() => {console.log('Button clicked with text:', text);}, [text]);return (<div><p>Count: {count}</p><p>Text: {text}</p><button onClick={() => setCount(count + 1)}>Increment Count</button><button onClick={() => setText(text === 'Hello' ? 'World' : 'Hello')}>Change Text</button><ChildComponent onClick={handleClick} /></div>);
};export default ParentComponent;

在這個示例中,handleClick 函數依賴于 text 狀態。每當 text 狀態發生變化時,handleClick 函數都會重新創建。這確保了 handleClick 始終使用最新的 text 值。

何時使用 useCallback

useCallback 在以下情況下特別有用:

  • 傳遞回調函數給子組件,且子組件依賴于回調函數的引用。
  • 回調函數在組件重新渲染時不需要重新創建。
  • 優化性能,防止因函數引用變化導致的子組件不必要的重新渲染。

注意事項

  • useCallback 僅用于記憶化回調函數,避免不必要的函數重新創建和子組件重新渲染。
  • 過度使用 useCallback 可能導致代碼復雜性增加,應根據實際需要使用。
  • 記憶化函數會占用內存,特別是當依賴項變化頻繁時,可能會影響性能。

示例總結

以下是一個使用 useCallback 優化回調函數的完整示例:

import React, { useState, useCallback } from 'react';const ChildComponent = React.memo(({ onClick }) => {console.log('Rendering ChildComponent');return <button onClick={onClick}>Click me</button>;
});const ParentComponent = () => {const [count, setCount] = useState(0);const handleClick = useCallback(() => {console.log('Button clicked');}, []);return (<div><p>Count: {count}</p><button onClick={() => setCount(count + 1)}>Increment Count</button><ChildComponent onClick={handleClick} /></div>);
};export default ParentComponent;

在這個示例中,handleClick 函數使用了 useCallback,確保在父組件重新渲染時不會重新創建,從而避免了子組件的重新渲染,提高了性能。

7.useMemo

useMemo定義

useMemo是 React 框架中的一個重要 Hook,它的核心目的是通過緩存計算結果,避免在組件渲染時進行不必要的重復計算,從而優化性能。這意味著只有當其依賴項發生變化時,useMemo才會重新計算這個值,否則它將重用之前的結果。

它的基本使用格式如下:

const cachedValue = useMemo(calculateValue, dependencies)
  • calculateValue:這是一個用于計算我們想要緩存的值的函數。為了確保結果的穩定性和預測性,這個函數應該是一個純函數。這意味著,它在相同的輸入下總是返回相同的輸出,并且沒有任何副作用。
  • dependencies:這是一個數組,包含useMemo所依賴的變量或值。當數組中的任何值發生變化時,calculateValue函數將被重新執行。

useMemo基礎用法

useMemo 接受兩個參數:一個函數和一個依賴項數組。

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

在上面的例子中,computeExpensiveValue是一個可能需要很長時間來計算的函數。我們只有當ab改變時,才重新調用這個函數。否則,我們會使用之前緩存的值。

用一個例子來看 useMemo 的執行時機:

import React, { useMemo, useState } from "react";function filterUsers(users, searchTerm) {return users.filter((user) => user.name.includes(searchTerm));
}function useMemoDemo() {const [searchTerm, setSearchTerm] = useState("");const [isDark, setIsDark] = useState(false);const allUsers = useMemo(() => {let list = [];for (let i = 1; i <= 500; i++) {list.push({ id: i, name: `User${i}` });}return list;}, []);const useMemoCurrentUsers = useMemo(() => {console.log('with useMemo')return filterUsers(allUsers, searchTerm);}, [allUsers, searchTerm]);return (<div>{/* 每一次更改查詢框內容,都會觸發useMemo */}<inputvalue={searchTerm}onChange={(e) => setSearchTerm(e.target.value)}placeholder="Search by name..."/>{/* 每一次更改背景色,都不會觸發useMemo */}<button onClick={() => setIsDark((pre) => !pre)}>{isDark ? "Dark mode" : "Light mode"}</button><div><div><h2>With useMemo</h2><div style={{ background: isDark ? "#000" : "" }}>{useMemoCurrentUsers.map((user) => (<div key={user.id}>{user.name}</div>))}</div></div></div></div>);
}export default useMemoDemo;

在這里簡單的示例中,每次修改查詢框的內容,都會觸發searchTerm的變化,進而觸發useMemo重新計算;而點擊切換背景色的按鈕,因為useMemo的依賴項沒有更新,所以不會觸發useMemo重新計算,而是直接使用上一次計算的返回值。

React.memo

React.memo 是 React 中的一個高階組件(HOC),它通過記憶化函數組件的結果來優化性能,防止不必要的重新渲染。它的工作原理是僅在組件的 props 發生變化時才重新渲染,這對依賴大量 props 且不需要每次父組件重新渲染時都重新渲染的組件特別有用。

使用方法

以下是 React.memo 的基本用法示例:

import React from 'react';// 一個接收 props 的函數組件
const MyComponent = (props) => {console.log('Rendering MyComponent');return (<div>{props.text}</div>);
};// 使用 React.memo 包裝組件
const MemoizedComponent = React.memo(MyComponent);export default MemoizedComponent;

在這個示例中,MyComponent 只有在 text prop 發生變化時才會重新渲染。如果父組件重新渲染,但 text prop 保持不變,MyComponent 不會重新渲染。

自定義比較函數

默認情況下,React.memo 進行的是淺比較。如果你需要進行更深層次的比較或有復雜的 props,可以提供自定義的比較函數作為 React.memo 的第二個參數:

import React from 'react';const MyComponent = (props) => {console.log('Rendering MyComponent');return (<div>{props.text}</div>);
};const areEqual = (prevProps, nextProps) => {// 在這里進行自定義比較return prevProps.text === nextProps.text;
};const MemoizedComponent = React.memo(MyComponent, areEqual);export default MemoizedComponent;

在這個示例中,areEqual 是一個函數,它接收前一個和下一個 props 并返回 true 表示它們相等,從而防止重新渲染。如果返回 false,組件將會重新渲染。

何時使用 React.memo

React.memo 在以下情況下特別有用:

  • 組件是純函數組件,并且其輸出完全由 props 決定。
  • 組件頻繁重新渲染,并且 props 保持不變。
  • 你想要優化性能,避免不必要的重新渲染。

注意事項

  • React.memo 只適用于函數組件。
  • 默認情況下,它進行淺比較,對于復雜的 props 可能不夠。
  • 過度使用 React.memo 可能會導致不必要的復雜性。應根據實際情況使用,重點放在那些實際受益于記憶化的組件上。

實例

以下是一個在父組件中使用 React.memo 的實際示例:

import React, { useState } from 'react';const ChildComponent = React.memo(({ text }) => {console.log('Rendering ChildComponent');return <div>{text}</div>;
});const ParentComponent = () => {const [count, setCount] = useState(0);const [text, setText] = useState('Hello');return (<div><button onClick={() => setCount(count + 1)}>Increment Count</button><button onClick={() => setText(text === 'Hello' ? 'World' : 'Hello')}>Change Text</button><ChildComponent text={text} /></div>);
};export default ParentComponent;

在這個示例中,ChildComponent 只有在 text prop 發生變化時才會重新渲染,即使 ParentComponent 因為 count 狀態變化而重新渲染。這可以幫助在大型應用中提高性能。

8.useDeferredValue

useDeferredValue 是 React 18 引入的一個 Hook,用于延遲更新狀態值,以提升用戶界面的響應速度。在處理高優先級任務(如輸入或交互)時,可以使用 useDeferredValue 將低優先級的狀態更新推遲,以保持界面的流暢性。

基本用法

以下是 useDeferredValue 的基本用法示例:

import React, { useState, useDeferredValue, useEffect } from 'react';const SearchComponent = () => {const [query, setQuery] = useState('');const deferredQuery = useDeferredValue(query);useEffect(() => {// 模擬一個數據獲取函數const fetchData = async () => {console.log('Fetching data for:', deferredQuery);// 模擬數據獲取延遲await new Promise(resolve => setTimeout(resolve, 1000));console.log('Data fetched for:', deferredQuery);};if (deferredQuery) {fetchData();}}, [deferredQuery]);return (<div><inputtype="text"value={query}onChange={(e) => setQuery(e.target.value)}placeholder="Type your search query..."/><p>Searching for: {deferredQuery}</p></div>);
};export default SearchComponent;

在這個示例中:

  1. useState 用于創建一個 query 狀態,表示用戶輸入的查詢。
  2. useDeferredValue 用于創建一個 deferredQuery,它是 query 的延遲副本。
  3. 當用戶輸入內容時,query 狀態立即更新,而 deferredQuery 會稍后更新。
  4. useEffect 鉤子監聽 deferredQuery 的變化,并在其變化時執行數據獲取操作。

延遲更新的好處

使用 useDeferredValue 可以提高用戶界面的響應速度,特別是在處理大量數據或復雜計算時。通過將低優先級的狀態更新推遲,React 可以優先處理用戶輸入和交互,從而保持界面的流暢性。

注意事項

  • useDeferredValue 僅在 React 18 及更高版本中可用。
  • useDeferredValue 適用于那些不需要立即更新的狀態值,例如搜索查詢、過濾條件等。
  • useDeferredValue 不會阻止狀態的最終更新,只是將其延遲到更適當的時機。

示例:過濾列表

以下是一個使用 useDeferredValue 進行列表過濾的示例:

import React, { useState, useDeferredValue } from 'react';const ListComponent = ({ items }) => {const [filter, setFilter] = useState('');const deferredFilter = useDeferredValue(filter);const filteredItems = items.filter(item =>item.toLowerCase().includes(deferredFilter.toLowerCase()));return (<div><inputtype="text"value={filter}onChange={(e) => setFilter(e.target.value)}placeholder="Filter items..."/><ul>{filteredItems.map((item, index) => (<li key={index}>{item}</li>))}</ul></div>);
};export default ListComponent;

在這個示例中:

  1. useState 用于創建一個 filter 狀態,表示用戶輸入的過濾條件。
  2. useDeferredValue 用于創建一個 deferredFilter,它是 filter 的延遲副本。
  3. filteredItems 列表根據 deferredFilter 進行過濾,以提升用戶輸入的響應速度。

通過這種方式,可以在處理較大數據集時保持界面的流暢性,同時確保最終的過濾結果是準確的。

9.useTransition

useTransition 是 React 18 引入的一個 Hook,用于管理狀態更新的優先級。它允許你將某些狀態更新標記為“過渡”,這樣 React 就可以優先處理更高優先級的任務(如用戶輸入),而將過渡狀態的更新推遲到更適合的時間點。這對于保持用戶界面的響應速度非常有用。

基本用法

以下是 useTransition 的基本用法示例:

import React, { useState, useTransition } from 'react';const TransitionComponent = () => {const [isPending, startTransition] = useTransition();const [input, setInput] = useState('');const [list, setList] = useState([]);const handleChange = (e) => {const value = e.target.value;setInput(value);// 將狀態更新標記為過渡startTransition(() => {const newList = Array.from({ length: 20000 }, (_, index) => `${value} ${index}`);setList(newList);});};return (<div><input type="text" value={input} onChange={handleChange} />{isPending && <p>Loading...</p>}<ul>{list.map((item, index) => (<li key={index}>{item}</li>))}</ul></div>);
};export default TransitionComponent;

在這個示例中:

  1. useTransition 返回一個布爾值 isPending 和一個函數 startTransition
  2. isPending 表示過渡狀態是否正在進行中,可以用來顯示加載指示器。
  3. startTransition 用于將某些狀態更新標記為過渡。

用法說明

  • useTransition 的語法如下:

    const [isPending, startTransition] = useTransition();
    
  • isPending 是一個布爾值,指示過渡是否正在進行。

  • startTransition 是一個函數,用于包裹過渡狀態更新的邏輯。

處理大量數據

以下是一個處理大量數據的示例,通過 useTransition 保持界面的響應速度:

import React, { useState, useTransition } from 'react';const BigDataComponent = () => {const [isPending, startTransition] = useTransition();const [query, setQuery] = useState('');const [results, setResults] = useState([]);const handleSearch = (e) => {const value = e.target.value;setQuery(value);startTransition(() => {// 模擬數據處理const filteredResults = mockData.filter(item => item.includes(value));setResults(filteredResults);});};return (<div><input type="text" value={query} onChange={handleSearch} placeholder="Search..." />{isPending && <p>Loading results...</p>}<ul>{results.map((result, index) => (<li key={index}>{result}</li>))}</ul></div>);
};const mockData = Array.from({ length: 10000 }, (_, index) => `Item ${index}`);export default BigDataComponent;

在這個示例中:

  1. 當用戶輸入搜索查詢時,handleSearch 函數會立即更新 query 狀態。
  2. startTransition 用于標記數據過濾操作為過渡狀態,從而使得 React 可以優先處理用戶輸入,并推遲執行較重的數據處理任務。
  3. 如果過渡狀態正在進行,isPendingtrue,顯示加載指示器。

注意事項

  • useTransition 適用于那些可以延遲更新的狀態,例如數據過濾、分頁加載等。
  • startTransition 包裹的狀態更新不會阻塞用戶輸入或其他高優先級任務,從而提升用戶體驗。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/45398.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/45398.shtml
英文地址,請注明出處:http://en.pswp.cn/web/45398.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

存儲實驗:Linux掛載iscsi硬盤與華為OceanStor創建LUN全流程

目錄 目的環境規劃實驗實驗流程Centos配置0. 關閉防火墻1. 設置網卡信息2. 配置路由3. iscsiadm連接存儲 iSCSI LUN創建&#xff08;以華為OceanStor為例&#xff09;驗證1. 驗證是否成功2. 開啟自動掛載 目的 實現Linux連接iscsi硬盤&#xff0c;同時實現開機自啟掛載 環境規…

掌握本地倉儲:Gradle本地倉庫配置全指南

掌握本地倉儲&#xff1a;Gradle本地倉庫配置全指南 在構建自動化的領域中&#xff0c;Gradle以其靈活性和強大的依賴管理功能脫穎而出。管理項目依賴時&#xff0c;經常需要配置本地倉庫以優化構建速度、控制依賴版本或支持離線構建。本文將深入探討如何在Gradle中配置本地倉…

JAVA----泛型

泛型 認識泛型 定義類、接口、方法時&#xff0c;同時聲明了一個或者多個類型變量&#xff08;如&#xff1a;&#xff09; &#xff0c;稱為泛型類、泛型接口&#xff0c;泛型方法、它們統稱為泛型。 作用&#xff1a;利用泛型&#xff0c;可以限制集合存儲數據的類型. 泛型…

Gitee簡易使用流程(后期優化)

目錄 1.修改用戶名 2.文件管理 新建文件/文件夾流程如下&#xff1a; 上傳文件流程如下&#xff1a; 以主頁界面為起點 1.修改用戶名 點解右上角的頭像--> 點擊“賬號設置” 點擊左邊欄里的“個人資料“ 直接修改用戶名即可 2.文件管理 選擇一個有修改權限倉庫&#…

【從0到1進階Redis】主從復制

筆記內容來自B站博主《遇見狂神說》&#xff1a;Redis視頻鏈接 1、概念 主從復制&#xff0c;是指將一個臺 Redis 服務器的數據&#xff0c;復制到其他的 Redis 服務器。前者稱為主節點&#xff08;master/leader&#xff09;&#xff0c;后者稱為從節點&#xff08;slave/foll…

this指向解析

先看題目&#xff1a; 第一題&#xff1a; var name window var person1 { name: person1, show1: function () { console.log(this.name) }, show2: () > console.log(th show3: function () { return function () { …

MFC之對話框--重繪元文件

文章目錄 實現示例展示需要繪制的窗口/位置控件位置更新下一次示例粗細滑動部分更新 重繪元文件&#xff08;窗口變化內容消失&#xff09;方法一&#xff1a;使用元文件方法二&#xff1a;兼容設備方法三&#xff1a;使用自定義類存儲繪圖數據除畫筆外功能處理畫筆功能處理 保…

springmvc1

以前的servlet程序&#xff1a; springmvc 不同的處理器&#xff1a;不同的方法或者處理類 所有的請求都會經過dispathcherservlet的doservice方法&#xff1a; mvc原理&#xff1a; 前端控制器&#xff1a;jsp或者什么東西

Python字符串基礎與高級操作

在Python中&#xff0c;字符串是不可變的數據類型&#xff0c;用于存儲一系列的字符。它們可以被創建、訪問、操作和格式化&#xff0c;但一旦創建&#xff0c;其內容就不能改變。下面是一篇關于Python字符串技術的詳細講解&#xff0c;包括創建、訪問、更新、轉義、運算符、格…

Phpstudy 2018 之xhcms搭建

1、由于直接訪問根目錄無法進入網站 2、所以采用搭建網站&#xff0c;第一使用系統服務模式、選擇php-5.4.45Apache模式 3、網站域名為本地ip地址或者127.0.0.1、端口8085 4、在navicat創建名字為xjcms的數據庫&#xff0c;并導入sql數據庫文件 5、瀏覽器輸入127.0.0.1:8085直接…

中風傷寒、感冒、六經辨證筆記

目錄 基礎傳經的原因傳經的過程及速度傳經的危害感冒時體痛頭痛的原因根據頭痛的位置辨經 太陽病太陽中風外風內熱 表虛感冒顆粒&#xff08;桂枝葛根湯&#xff09; 少陽病辨病總結傷寒論原文半表半里太陽為開&#xff0c;陽明為闔&#xff0c;少陽為樞膽的作用幫助腸胃消化、…

deepstream讀取mp4文件及不同類型視頻輸入bug解決

在deepstream中使用mp4文件&#xff0c;與rtsp類似&#xff0c;使用uridecodebin即可&#xff0c;&#xff08;可見官方test.py文件&#xff09; def create_source_bin(index, uri):print("Creating source bin")# Create a source GstBin to abstract this bins c…

定投投什么?

定投可以選擇的品種有銀行理財和基金 銀行理財目前有的品種有期限限制&#xff0c;不是那么公開的特點。如果說你想通過定投積累一筆低風險的&#xff0c;用于應急或者短期內要用的錢&#xff0c;可以選擇定投現金類銀行理財。 基金是最適合定投的產品&#xff0c; 基金分為…

【自然語言處理】面向新冠肺炎的社會計算應用

面向新冠肺炎的社會計算應用 1 任務目標 1.1 案例簡介 新冠肺炎疫情牽動著我們每一個人的心&#xff0c;在這個案例中&#xff0c;我們將嘗試用社會計算的方法對疫情相關的新聞和謠言進行分析&#xff0c;助力疫情信息研究。本次作業為開放性作業&#xff0c;我們提供了疫情…

C++ STL stable_sort用法

一&#xff1a;功能 對區間內元素進行排序&#xff0c;保證相等元素的順序&#xff08;穩定排序&#xff09; 二&#xff1a;用法 #include <iostream>struct Record {std::string label;int rank; };int main() {std::vector<Record> data {{"q", 1},…

代碼隨想錄第五十一天 | 300.最長遞增子序列 , 674. 最長連續遞增序列 , 718. 最長重復子數組

300.最長遞增子序列 看完想法&#xff1a;在dp遞推公式那里沒有太看得懂。首先dp【i】的狀態肯定是由前面的dp【0】到dp【i-1】推出的&#xff0c;但是dp【0】到dp【i-1】可以推出dp【i】有個前提就是(nums【i】 > nums【0到i-1任意一個】),例如nums【1】 2, nums【3】 5…

Tomcat下載安裝配置教程(零基礎超詳細)

「作者簡介」&#xff1a;冬奧會網絡安全中國代表隊&#xff0c;CSDN Top100&#xff0c;就職奇安信多年&#xff0c;以實戰工作為基礎著作 《網絡安全自學教程》&#xff0c;適合基礎薄弱的同學系統化的學習網絡安全&#xff0c;用最短的時間掌握最核心的技術。 Tomcat 1、下載…

外包干了1個月,技術明顯退步。。。

有一種打工人的羨慕&#xff0c;叫做“大廠”。 真是年少不知大廠香&#xff0c;錯把青春插稻秧。 但是&#xff0c;在深圳有一群比大廠員工更龐大的群體&#xff0c;他們頂著大廠的“名”&#xff0c;做著大廠的工作&#xff0c;還可以享受大廠的伙食&#xff0c;卻沒有大廠…

【輕松拿捏 】Java-static關鍵字(面試)

Java-static關鍵字 1. 定義和基本概念 回答要點&#xff1a; 示例回答&#xff1a; 2. static 變量 回答要點&#xff1a; 示例回答&#xff1a; 代碼示例&#xff1a; 3. static方法 回答要點&#xff1a; 示例回答&#xff1a; 代碼示例&#xff1a; 4. static 代…

Modbus協議簡介與Python實現

Modbus協議是工業自動化和控制系統中廣泛使用的通信協議。自1979年由Modicon(現為施耐德電氣的一部分)引入以來,它已經成為一種標準的通信協議,用于連接電子設備和傳感器。Modbus協議基于主從架構,支持多種物理層和傳輸模式,如串行通信(RS-232/RS-485)和以太網。 1. Mo…