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.mzph.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;同时实现开机自启挂载 环境规…

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…

MFC之对话框--重绘元文件

文章目录 实现示例展示需要绘制的窗口/位置控件位置更新下一次示例粗细滑动部分更新 重绘元文件&#xff08;窗口变化内容消失&#xff09;方法一&#xff1a;使用元文件方法二&#xff1a;兼容设备方法三&#xff1a;使用自定义类存储绘图数据除画笔外功能处理画笔功能处理 保…

springmvc1

以前的servlet程序&#xff1a; springmvc 不同的处理器&#xff1a;不同的方法或者处理类 所有的请求都会经过dispathcherservlet的doservice方法&#xff1a; mvc原理&#xff1a; 前端控制器&#xff1a;jsp或者什么东西

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;少阳为枢胆的作用帮助肠胃消化、…

【自然语言处理】面向新冠肺炎的社会计算应用

面向新冠肺炎的社会计算应用 1 任务目标 1.1 案例简介 新冠肺炎疫情牵动着我们每一个人的心&#xff0c;在这个案例中&#xff0c;我们将尝试用社会计算的方法对疫情相关的新闻和谣言进行分析&#xff0c;助力疫情信息研究。本次作业为开放性作业&#xff0c;我们提供了疫情…

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…

STM32智能交通监测系统教程

目录 引言环境准备智能交通监测系统基础代码实现&#xff1a;实现智能交通监测系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;交通监测与管理问题解决方案与优化收尾与总结 1. 引言 智能交通监测系统通…

Linux--线程池(包含日志的解释)

线程系列&#xff1a; Linux–线程的认识(一) Linux–线程的分离、线程库的地址关系的理解、线程的简单封装&#xff08;二&#xff09; 线程的互斥&#xff1a;临界资源只能在同一时间被一个线程使用 生产消费模型 信号量 线程池 线程池&#xff08;Thread Pool&#xff09;是…

Qt 统计图编程

学习目标&#xff1a;Qt 折线图&#xff0c;柱形图和扇形统计图编程 学习基础 Qt QChart 曲线图表操作-CSDN博客 学习内容 Qt中绘制三种常见的图表非常方便, 主要步骤如下: 1. 折线图: - 使用QLineSeries定义折线数据,添加多个坐标点 - 使用QValueAxis创建X轴和Y轴 - 将…

拷贝文件的一些操作

利用fputc 、fgetc实现文件的拷贝 int main(int argc, const char *argv[]) {FILE* rfpfopen(argv[1],"r");FILE* wfpfopen(argv[2],"w");if(rfpNULL || wfpNULL){perror("fopen");return 1;}while(1){char resfgetc(rfp);if(feof(rfp)1){break;…

PointCloudLib LocalMaximum_DeleteMaxPoint C++版本

测试效果 简介 在点云库&#xff08;Point Cloud Library&#xff0c;PCL&#xff09;中&#xff0c;处理点云数据时&#xff0c;经常需要去除局部最大点&#xff08;Local Maximum&#xff09;&#xff0c;这通常用于去除噪声、提取特定形状的特征或者简化点云数据。局部最大…

[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-14 SPI MASET发送程序设计

软件版本&#xff1a;Anlogic -TD5.9.1-DR1_ES1.1 操作系统&#xff1a;WIN10 64bit 硬件平台&#xff1a;适用安路(Anlogic)FPGA 实验平台&#xff1a;米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台&#xff1a;https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…

数据库管理-第220期 Oracle的高可用-03(20240715)

数据库管理220期 2024-07-15 数据库管理-第220期 Oracle的高可用-03&#xff08;20240715&#xff09;1 AC/TAC2 配置Service3 用户权限4 端口开放总结 数据库管理-第220期 Oracle的高可用-03&#xff08;20240715&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文…

51单片机10(蜂鸣器介绍)

一、蜂鸣器介绍&#xff1a; 1、蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于电子产品中作为发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器。 &#xff08;1&#xff09;压电式蜂鸣器&#xff0c;它主要由多谐的一个增胀器…