React基础学习-Day04
React基础学习-Day04
常见的钩子函数及基础使用方式
1.useState
useState
是 React 的一个 Hook,用于在函数组件中添加状态。它返回一个状态变量和一个更新该状态的函数。与类组件的 this.state
和 this.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
的状态变量,并将其初始值设为 0
。setCount
是更新 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
来管理两个独立的状态变量 count
和 text
,并使用函数式更新确保状态更新基于最新的状态值。这有助于避免状态更新的竞争条件。
ps:useState的第一个参数用函数可以使用异步函数吗
useState
的第一个参数是初始状态,它可以是一个值或者是一个函数,但这个函数必须是同步的。React 不支持在 useState
的初始状态函数中使用异步函数。useState
的初始状态函数只会在组件的初始渲染时被调用一次,并且它是同步的。
如果你需要在组件初始化时从异步操作中获取数据并设置状态,你可以在 useEffect
Hook 中执行异步操作,并在操作完成后更新状态。
以下是一个如何使用 useEffect
和 useState
来处理异步操作的示例:
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;
在这个示例中:
useState
用于管理组件的状态,包括data
、loading
和error
。useEffect
用于执行异步数据获取操作。当组件首次挂载时,这个 effect 会运行,触发fetchData
异步函数。fetchData
异步函数使用fetch
从 API 获取数据。如果成功,数据将被存储在data
状态中,并且loading
状态被设置为false
。如果出现错误,error
状态将被设置,并且loading
状态同样被设置为false
。
这样,虽然不能在 useState
的初始状态函数中使用异步函数,但可以通过 useEffect
来实现异步数据获取和状态管理。
2.useEffect
useEffect
是 React 的一个 Hook,用于在函数组件中执行副作用操作。副作用操作包括数据获取、订阅、手动 DOM 操作以及在组件更新或卸载时执行清理任务。useEffect
可以看作是 componentDidMount
、componentDidUpdate
和 componentWillUnmount
的组合。
基本用法
以下是 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;
在这个示例中:
ThemeContext
是一个上下文对象,通过createContext()
创建。ThemeProvider
是一个提供者组件,使用ThemeContext.Provider
包装其子组件,并通过value
属性传递theme
和toggleTheme
函数。ThemeConsumerComponent
是一个消费者组件,使用useContext(ThemeContext)
来订阅ThemeContext
上下文。它从上下文中读取theme
和toggleTheme
,并在按钮点击时切换主题。
注意事项
- 使用
createContext()
创建上下文对象。 - 使用
Provider
组件包裹子组件,并通过value
属性传递数据。 - 在需要访问上下文数据的组件中使用
useContext(ThemeContext)
。
多个上下文
您可以在应用程序中使用多个上下文。每个上下文对象都应该有自己的提供者和消费者组件。React 会确保正确的数据传递和更新。
总结
useContext
Hook 提供了一种轻松地在函数组件中使用 React 上下文的方式。它使得组件可以更简洁地订阅和使用跨组件树的数据。通过 useContext
,您可以避免 props drilling,提高组件的可重用性和可维护性。
4.useReducer
一、介绍useReducer的重要性和优势
useReducer是 React 中的一个 Hook,用于管理应用状态。它提供了一种更简洁、更易于理解的方式来处理复杂的状态逻辑。
重要性:
状态管理:在 React 应用中,状态管理是至关重要的。useReducer允许开发者以更清晰和集中的方式管理应用的状态。
复杂状态逻辑:对于涉及多个状态变量和复杂的更新逻辑的场景,使用useReducer可以更好地组织和维护代码。
可预测的状态更新:useReducer使用函数来更新状态,这使得状态更新更加可预测和易于理解。
更好的代码可读性:通过使用useReducer,可以将状态更新逻辑拆分为独立的函数,提高代码的可读性和可维护性。
优势:
简化代码:相比使用多个 useState 钩子来管理复杂的状态,useReducer 可以减少代码的冗余。
更好的性能:useReducer在某些情况下可以提供更好的性能,尤其是在处理大量状态更新时。
状态的合并:useReducer支持合并多个更新操作,从而减少不必要的重新渲染。
清晰的状态更新逻辑:使用useReducer可以将状态更新逻辑放在一个单独的函数中,使其更加清晰和易于理解。
总之,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 对象在组件的整个生命周期中保持不变,不会因重新渲染而重新创建。
主要用途
-
获取 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()
方法来聚焦输入框。 -
保存任意可变值
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
来更新计数器的值,而不需要触发组件的重新渲染。 -
保存上一个 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
是一个可能需要很长时间来计算的函数。我们只有当a
或b
改变时,才重新调用这个函数。否则,我们会使用之前缓存的值。
用一个例子来看 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;
在这个示例中:
useState
用于创建一个query
状态,表示用户输入的查询。useDeferredValue
用于创建一个deferredQuery
,它是query
的延迟副本。- 当用户输入内容时,
query
状态立即更新,而deferredQuery
会稍后更新。 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;
在这个示例中:
useState
用于创建一个filter
状态,表示用户输入的过滤条件。useDeferredValue
用于创建一个deferredFilter
,它是filter
的延迟副本。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;
在这个示例中:
useTransition
返回一个布尔值isPending
和一个函数startTransition
。isPending
表示过渡状态是否正在进行中,可以用来显示加载指示器。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;
在这个示例中:
- 当用户输入搜索查询时,
handleSearch
函数会立即更新query
状态。 startTransition
用于标记数据过滤操作为过渡状态,从而使得 React 可以优先处理用户输入,并推迟执行较重的数据处理任务。- 如果过渡状态正在进行,
isPending
为true
,显示加载指示器。
注意事项
useTransition
适用于那些可以延迟更新的状态,例如数据过滤、分页加载等。startTransition
包裹的状态更新不会阻塞用户输入或其他高优先级任务,从而提升用户体验。
10.useImperativeHandle
在 React 中,useImperativeHandle
是一个自定义 Hook,用于自定义暴露给父组件的实例值或者方法。它通常与 forwardRef
结合使用,用于提供更精细的控制和优化。
基本用法
useImperativeHandle
允许你在函数组件中指定对外暴露的实例值或者方法,通常用于封装底层 DOM 元素或者其他组件实例,从而提供更直接的访问接口给父组件。
import React, { useRef, useImperativeHandle, forwardRef } from 'react';// 子组件
const ChildComponent = forwardRef((props, ref) => {const childRef = useRef(null);// 通过 useImperativeHandle 暴露给父组件的实例值或者方法useImperativeHandle(ref, () => ({// 在这里定义暴露给父组件的实例值或者方法focus: () => {childRef.current.focus();},// 可选的清理函数// cleanup: () => {// // 可选的清理逻辑// }}));return <input ref={childRef} />;
});// 父组件
const ParentComponent = () => {const childRef = useRef(null);const handleClick = () => {// 调用子组件暴露的方法childRef.current.focus();};return (<div><ChildComponent ref={childRef} /><button onClick={handleClick}>Focus Child Input</button></div>);
};export default ParentComponent;
使用说明
- forwardRef 和 useRef: 使用
forwardRef
和useRef
来创建一个能够获取子组件实例的 ref。 - useImperativeHandle Hook: 使用
useImperativeHandle
来定义在父组件中通过 ref 可以访问到的值或者方法。 - 实例值或者方法: 在
useImperativeHandle
的回调函数中,可以定义需要暴露给父组件的实例值或者方法。这些值或者方法可以是直接操作 DOM 元素的方法、状态管理等。 - 清理函数(可选):
useImperativeHandle
的第二个参数可以是一个可选的清理函数,用于在组件卸载或者 ref 变化时执行一些清理逻辑。
注意事项
useImperativeHandle
通常用于需要在父组件中操作子组件实例的情况,例如直接访问子组件的 DOM 元素或者调用子组件的方法。- 合理使用
useImperativeHandle
可以提高代码的可读性和灵活性,但要避免滥用,过多暴露子组件的内部实现可能会导致组件耦合性增加,不利于组件复用和维护。
总结
useImperativeHandle
是一个用于自定义暴露给父组件的实例值或者方法的自定义 Hook,在处理需要直接访问子组件实例的情况时非常有用。它提供了一种灵活的方式来管理和操作组件实例,使得组件之间的交互更加直接和高效。
相关文章:
React基础学习-Day04
React基础学习-Day04 常见的钩子函数及基础使用方式 1.useState useState 是 React 的一个 Hook,用于在函数组件中添加状态。它返回一个状态变量和一个更新该状态的函数。与类组件的 this.state 和 this.setState 相对应,useState 让函数组件也能拥有…...
python爬虫获取网易云音乐评论歌词以及歌曲地址
python爬虫获取网易云音乐评论歌词以及歌曲地址 一.寻找数据接口二.对负载分析三.寻找参数加密过程1.首先找到评论的请求包并找到发起程序2.寻找js加密的代码 四.扣取js的加密源码1.加密函数参数分析①.JSON.stringify(i0x)②bse6Y(["流泪", "强"])③bse6Y…...
中间件的理解
内容来源于学习网站整理。【一看就会】什么是前端开发的中间件?_哔哩哔哩_bilibili 每日八股文~白话说mq,消息中间件_哔哩哔哩_bilibili 例如: 1)两个人打电话,中间的通信网络就是中间件。 2)菜鸟驿站&…...
django实现用户的注册、登录、注销功能
创建django项目的步骤:Django项目的创建步骤-CSDN博客 一、前置工作 配置数据库,设置数据库引擎为mysql 1、在settings文件中找到DATABASES, 配置以下内容 DATABASES {"default": {ENGINE: django.db.backends.mysql, # 数据库引擎NAME: dja…...
【JAVA 常用API】数据库字段存储JSON格式数据,JAVA中如何将List<Entity>或者对象实体转换为字符串
在Java中,可以使用第三方库如Jackson或Gson来进行JSON的转换。这些库提供了将Java对象转换为JSON字符串的方法。 一:Gson API转Json Getter Setter NoArgsConstructor AllArgsConstructor ToString Builder public class Person {private String name;p…...
AI算不出9.11和9.9哪个大?六家大模型厂商总结了这些原因
大模型“答对”或“答错”其实是个概率问题。关于“9.11和9.9哪个大”,这样一道小学生难度的数学题难倒了一众海内外AI大模型。7月17日,第一财经报道了国内外“12个大模型8个都会答错”这道题的现象,大模型的数学能力引发讨论。 “从技术人员…...
MacBook电脑远程连接Linux系统的服务器方法
一、问题简介 Windows 操作系统的电脑可使用Xshell等功能强大的远程连接软件。通过连接软件,用户可以在一台电脑上访问并控制另一台远程计算机。这对于远程技术支持、远程办公等场景非常有用。但是MacBook电脑的macOS无法使用Xshell。 在Mac上远程连接到Windows服…...
CSS-0_3 CSS和单位
文章目录 CSS的值和单位属性值长度单位CSS和绝对单位CSS和相对单位百分比em & rem视口 颜色单位 碎碎念 CSS的值和单位 我们知道,CSS是由属性和属性值所组成的表 随着CSS的发展,属性不说几千也有几百,我从来不支持去背诵所有的可能性。…...
【代码随想录|贪心算法 455. 分发饼干 376. 摆动序列 53. 最大子数组和】
代码随想录|贪心算法 455. 分发饼干 一、455. 分发饼干1.代码2.问题 二、376. 摆动序列1.代码 三、53. 最大子数组和1.代码 总结 python 一、455. 分发饼干 455. 分发饼干 1.代码 代码如下(示例): class Solution:def findContentChildr…...
swift小知识点(二)
1、 Swift 枚举 Swift 中使用 enum 关键词来创建枚举并且把它们的整个定义放在一对大括号内: enum enumname {// 枚举定义放在这里 } 如下事例: // 定义枚举 enum DaysofaWeek {case Sundaycase Mondaycase TUESDAYcase WEDNESDAYcase THURSDAYcase…...
机器人产业发展格局多元化,创业公司突破瓶颈需多维施策
当前,机器人产业的发展格局呈现出多元化、快速增长和技术不断创新的特点。从全球视角来看,机器人市场持续增长,预计到2026年全球人形机器人市场规模将超过20亿美元,到2030年有望突破200亿美元,显示出巨大的市场潜力和发…...
接口测试JMeter-1.接口测试初识
第一章 接口测试初识 1. 接口测试理论基础 “接口测试”一个让人觉得非常高大上的名词,特别是对于刚入门的测试同学而言。随着测试技术不断的深化,“接口测试”出现在我们视野中的频次越来越高。那么接口测试到底是如何做的?接口测试的优势又…...
[米联客-安路飞龙DR1-FPSOC] FPGA基础篇连载-22 TPG图像测试数据发生器设计
软件版本:Anlogic -TD5.9.1-DR1_ES1.1 操作系统:WIN10 64bit 硬件平台:适用安路(Anlogic)FPGA 实验平台:米联客-MLK-L1-CZ06-DR1M90G开发板 板卡获取平台:https://milianke.tmall.com/ 登录“米联客”FPGA社区 ht…...
如何通过企业微信会话存档保护企业利益?
赵总: 张经理,最近行业内频发数据泄露事件,我们的客户资料和内部沟通记录安全吗? 张经理: 赵总,我们已经采取了一系列措施来加强数据安全。特别是针对企业微信的沟通记录,我们最近引入了安企神软件,它能很…...
git修改提交姓名
git config --global user.name “新用户名” git config --global user.email “新邮箱地址” 修改提交的用户名 git config --global user.name “yu***”...
5、在共享内存无指针编程:句柄HANDLE转换为指针
初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的,可以在任何平台上使用。 共享内存里面不能用指针&#…...
hive动态分区导致xceivercount超限,hdfs无法创建新连接
目录 一、事件复盘: 二、解决方案: 三、讨论 一、事件复盘: hdfs无法创建新的文件,xceivercount超过最大设置,平时每个datanode只有100个左右的连接,突然达到8000以上。 事故原因,跨多天的…...
如何识别Android init 中的缓慢操作
Android 14 开机时间优化措施汇总-CSDN博客 Android 14 开机时间优化措施-CSDN博客根据systrace报告优化系统时需要关注的指标和优化策略-CSDN博客Android系统上常见的性能优化工具-CSDN博客Android上如何使用perfetto分析systrace-CSDN博客Android系统设置kernel log level的…...
JVM:常用工具总结
文章目录 一、jstat工具 一、jstat工具 Jstat工具是JDK自带的一款监控工具,可以提供各种垃圾回收、类加载、编译信息等不同的数据。使用方法为:jstat -gc进程ID每次统计的时间间隔(毫秒)统计次数。 C代表Capacity容量,…...
二染色,CF 1594D - The Number of Imposters
目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1594D - The Number of Imposters 二、解题报告 1、思路分析 并查集&…...
Go语言并发编程-Channel通信_2
Channel通信 Channel概述 不要通过共享内存的方式进行通信,而是应该通过通信的方式共享内存 这是Go语言最核心的设计模式之一。 在很多主流的编程语言中,多个线程传递数据的方式一般都是共享内存,而Go语言中多Goroutine通信的主要方案是Cha…...
Richteck立锜科技电源管理芯片简介及器件选择指南
一、电源管理简介 电源管理组件的选择和应用本身的电源输入和输出条件是高度关联的。 输入电源是交流或直流?需求的输出电压比输入电压高或是低?负载电流多大?系统是否对噪讯非常敏感?也许系统需要的是恒流而不是稳压 (例如 LED…...
Socket 简介与 Java Socket 编程示例
Socket(套接字)是网络通信中的一个关键概念,它是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。 一、定义与概念 基本概念:Socket可以被视为网络环境中进程间通信的API(应用程序编程接口)&…...
跟着操作,解决iPhone怎么清理内存难题
在如今智能手机功能日益强大的时代,我们使用手机拍照、录制视频、下载应用、存储文件等操作都会占用手机内存。当内存空间不足时,手机运行会变得缓慢,甚至出现卡顿、闪退等现象。因此,定期清理iPhone内存是非常必要的。那么&#…...
React、Vue的password输入框组件,如何关闭自动填充?
有时候我们的表单使用了一个password组件,这时候每次打开新建,都会自动获取浏览器缓存的密码,但是它的上一个input输入框并不是用户名,这时候我们希望我们的表单,每次点开的时候密码是空的,让用户自动输入&…...
HTML+JS+CSS计算练习
可填 题目数量 数字范围 计算符号 题目做完后会弹窗提示正确率、用时 效果图 源代码在图片后面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevic…...
设计模式使用场景实现示例及优缺点(行为型模式——责任链模式)
在一个遥远的森林深处,有一个和谐的动物王国。这个王国里的动物们都有各自的职责,大家相互合作,共同维护着森林的和平与繁荣。 一天,森林里来了一只迷路的小兔子,她焦急地四处张望,不知道该怎么办。于是&am…...
CSS-1_0 CSS和文档流
文章目录 CSS和文档流如何证明这个流的存在呢?流和display番外:inline-block 碎碎念 CSS和文档流 首先什么叫流呢? 通常来说,我们最终看到的网页是HTML文档中定义的各个元素挨个输出的结果,这种一个接一个输出的方式…...
小程序图片下载保存方法,图片源文件保存!
引言 现在很多时候我们在观看到小程序中的图片的时候,想保存图片的原文件格式的话,很多小程序是禁止保存的,即使是让保存的话,很多小程序也会限制不让保存原文件,只让保存一些分辨率很低的,非常模糊的图片…...
新书速览|深入理解Hive:从基础到高阶:视频教学版
《深入理解Hive:从基础到高阶:视频教学版》 本书内容 《深入理解Hive:从基础到高阶:视频教学版》采用“理论实战”的形式编写,通过大量的实例,结合作者多年一线开发实战经验,全面地介绍Hive的使用方法。《深入理解Hiv…...
批量导文章到wordpress/黄山seo公司
目前,******已成为一个很严重的网络问题。许多***甚至可以突破SSL加密和各种防火墙,攻入Web网站的内部,窃取信息。***可以仅凭借浏览器和几个技巧,即套取Web网站的客户信用卡资料和其它保密信息。因此,这无疑给网站建设…...
网站制作切图/拼多多seo是什么意思
在最近项目开发中遇到一个问题,情况是这样的,第一天数据库操作完全正常的,第二天早上来,就报错invalid connection。明明第一天都是正常的,第二天来就报错了,这个问题比较尴尬。于是上网搜索后,…...
垫江网站建设djrckj/站长工具关键词排名怎么查
对于给出的一组数据,要生成列表有以下方法:(1):>>>l []>>>for x in range(10):l.append(x) //追加元素到列表末尾print l[0,1,2,3,4,5,6,7,8,9](2):>>>l [x for x in range(10) if x%20] //我理解为先执…...
一个互联网公司可以做几个网站/百度提交网址多久才会收录
1. 首先明白什么是类加载器? 顾名思义,类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。一般来说,Java 虚拟机使用 Java 类的方式如下:Java 源程序(.java 文件)在经过 Jav…...
上海网站建设维护/seo是什么服
Flutter基础—你好,Flutter! Flutter基础—开发环境与入门 Flutter基础—第一个Flutter实例 Flutter基础—质感设计 Flutter基础—手势处理 Flutter基础—应用实例 Flutter基础—根据用户输入改变控件 Flutter基础—常用控件之容器 Flutter基础—…...
品牌网站建设定位/网络营销产品策略
文章目录一.基本介绍二.编写HelloWorld项目目录代码编译执行执行流程三.包,变量和函数1.包2.导入3.导出名4.函数5.命名返回值6.变量7.基本类型8.基本数据类型和String的转换8.1 基本类型8.2 转换9.零值10.类型转换11.类型推导12.常量13.数值常量14.值类型和引用类型14.1 值类型…...