跳到主要内容

React常用Hook

useState

useState 是 React 中的一个 Hook,用来在函数组件中添加状态管理。

例如下面的例子,count是当前的状态值, setCount 是更新状态的函数,0 是初始状态值。

function App() {
const [count, setCount] = useState(0)
return (
<>
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
</>
)
}

每次调用 setCount,React 会重新渲染组件,并且 count 的值会更新。 状态更新:setCount不是同步的,更新会在下一次渲染时生效。如果依赖当前状态来更新状态可以使用setCount(prev => prev + 1)

useEffect

useEffect 是 React 中的一个 Hook,用于处理副作用。副作用可以是数据获取、订阅、手动修改 DOM 等操作,它会在组件渲染之后执行。

useEffect(() => {
// 副作用代码
}, [dependencies]);
  • 第一个参数是一个函数,这个函数是副作用操作。
  • 第二个参数是一个依赖数组(dependencies),指定了副作用函数应该在哪些值变化时执行。如果数组为空,则副作用函数只会在组件挂载时执行一次。

注意点:

  • 如果依赖数组为空,则副作用函数只会在组件挂载时执行一次。
  • useEffect 不能直接返回一个 Promise,但是可以在副作用函数内部使用异步操作。

useLayoutEffect

useLayoutEffect 是 React 中的一个 Hook,和 useEffect 类似,但它的执行时机不同。它用于在 DOM 更新和浏览器绘制之前同步执行副作用。

主要区别:

  • useEffect 是在浏览器绘制之后执行副作用,通常用于处理异步操作或非同步渲染的副作用。
  • useLayoutEffect 则在 DOM 更新完成后,浏览器绘制之前执行副作用,这使得它适用于需要同步读取和修改 DOM 的场景。

注意:

  • 性能:useLayoutEffect 会同步执行,阻塞浏览器绘制,可能导致页面渲染延迟。在没有必要的情况下,最好使用 useEffect,以避免性能问题,一般来说,超过50ms就不要用了。
  • 服务端渲染(SSR):useLayoutEffect 在服务端渲染时无法执行,因为它依赖于浏览器环境

useReducer

useState的区别就是useReducer可以处理复杂的状态逻辑,所以如果简单就使用useState

useReducer 是 React 中的一个 Hook,通常用于处理复杂的状态逻辑,尤其是当状态更新逻辑比较复杂时,或者有多个状态依赖于某些条件时,useReduceruseState 更加适用。它是基于 Reducer 模式实现的,类似于 Redux 中的机制。

const [state, dispatch] = useReducer(reducer, initialState)
  • state: 当前的状态。
  • dispatch: 用来发送动作(action)的函数,触发状态更新。
  • reducer: 是一个函数,定义了如何根据传入的 action 来更新状态。
  • initialState: 初始化的状态。

reducer 是一个函数,接收两个参数:

  1. state: 当前的状态。
  2. action: 动作,通常是一个对象,包含 typepayload 等属性。

例子:

import React, { useReducer } from 'react';

const initialState = { count: 0, name: 'Alice' };

function reducer(state: any, action: any) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'decrement':
return { ...state, count: state.count - 1 };
case 'setName':
return { ...state, name: action.payload };
default:
return state;
}
}

function App() {
const [state, dispatch] = useReducer(reducer, initialState);

return (
<div>
<p>Count: {state.count}</p>
<p>Name: {state.name}</p>
<button onClick={() => dispatch({ type: 'increment' })}>增加</button>
<button onClick={() => dispatch({ type: 'decrement' })}>减少</button>
<button onClick={() => dispatch({ type: 'setName', payload: 'Bob' })}>修改名字</button>
</div>
);
}

export default App;
  • reducer 函数根据不同的 action.type 来更新 state
  • dispatch 被用来派发不同类型的 action,通过 dispatch 触发 reducer 函数,从而更新 state

useRef

useRef 是 React 中的一个 Hook,通常用于访问和操作 DOM 元素或保持一个不触发重新渲染的可变引用值

重点:

  • 使用 useRef 创建的引用不会触发组件的重新渲染。
  • 使用 useRef 创建的引用可以保持对 DOM 元素的引用,也可以保持对组件内部变量的引用。
import { useEffect, useRef } from "react";

function App() {
const inputRef = useRef<HTMLInputElement>(null);

useEffect(() => {
inputRef.current?.focus();
});

return (
<div>
<input ref={inputRef}></input>
</div>
);
}

export default App;

  • ref 是一个对象,包含 current 属性,current 属性是可变的,并且不会触发重新渲染。
  • initialValueref 的初始值。

forwardRef

forwardRef 是 React 中的一个高阶组件,用于将子组件的 ref 传递给父组件。用在需要父组件操作子组件的 ref 时。

import React, { useRef } from 'react';

// 使用 forwardRef 将 ref 转发到组件中的 div 元素
const MyComponent = React.forwardRef((props, ref) => {
return <div ref={ref}>{props.children}</div>;
});

function ParentComponent() {
const divRef = useRef(null);

const handleClick = () => {
// 通过 ref 直接访问 MyComponent 中的 div
console.log(divRef.current); // 输出 div DOM 元素
divRef.current.style.color = 'red';
};

return (
<div>
<MyComponent ref={divRef}>Hello, ForwardRef!</MyComponent>
<button onClick={handleClick}>查看 div 元素</button>
</div>
);
}

export default ParentComponent;

说明:

  1. 父组件通过 ref 直接访问子组件的 DOM 元素或组件实例。
  2. 子组件通过 forwardRefref 传递给父组件。
  3. 父组件通过 ref 直接访问子组件的 DOM 元素或组件实例。

useImperativeHandle

通常情况下,React 组件暴露给父组件的 ref 只是对组件 DOM 的引用,但有时我们希望通过 ref 传递一些自定义的实例方法或属性,而不只是 DOM 节点。这时候就可以使用 useImperativeHandle 来实现。

useImperativeHandle(ref, () => ({
// 定义暴露给父组件的属性或方法
}), [dependencies]);
  • ref: 父组件传递给子组件的 ref 对象。
  • () => ({}): 返回一个对象,对象中的属性或方法将被暴露给父组件。
  • [dependencies]: 依赖数组,当依赖发生变化时,useImperativeHandle 会重新执行。

例子:

import React, { useState, useRef, useImperativeHandle } from 'react';

// 子组件
const MyInput = React.forwardRef((props, ref) => {
const [value, setValue] = useState('');
const inputRef = useRef(null);

// 使用 useImperativeHandle 自定义暴露给父组件的方法
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus(); // 通过 ref 聚焦输入框
},
reset: () => {
setValue(''); // 重置输入框内容
},
getValue: () => value // 获取当前输入框的值
}));

return <input ref={inputRef} value={value} onChange={e => setValue(e.target.value)} />;
});

// 父组件
function App() {
const inputRef = useRef(null);

const handleFocus = () => {
inputRef.current.focus(); // 调用子组件暴露的 focus 方法
};

const handleReset = () => {
inputRef.current.reset(); // 调用子组件暴露的 reset 方法
};

const handleGetValue = () => {
alert(inputRef.current.getValue()); // 调用子组件暴露的 getValue 方法
};

return (
<div>
<MyInput ref={inputRef} />
<button onClick={handleFocus}>聚焦输入框</button>
<button onClick={handleReset}>重置输入框</button>
<button onClick={handleGetValue}>获取输入框内容</button>
</div>
);
}

export default App;

说明:

  1. MyInput 是一个子组件,使用 useImperativeHandle 暴露了 focus、reset 和 getValue 方法。
  2. 父组件通过 ref 直接访问子组件的 focus、reset 和 getValue 方法。

useImperativeHandle 通常与 forwardRef 一起使用,因为它需要访问由父组件传递下来的 refforwardRef 用来转发 ref,而 useImperativeHandle 用来自定义这个 ref 所暴露的实例方法。

useContext

useContextReact 中的一个 Hook,用于在组件中访问通过 Context API 提供的共享数据。它让你可以在组件树中跨层级传递数据,而不需要通过层层传递 props

例如:

import React, { createContext, useContext, useState } from 'react';
// 创建一个 Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
// 使用 Provider 提供 theme 值给子组件
<ThemeContext.Provider value={theme}>
<div>
<h1>当前主题: {theme}</h1>
<Button/>
<button onClick={()=>{setTheme(theme==='light'?'dark':'light')}}>
切换
</button>
</div>
</ThemeContext.Provider>
);
}

// 子组件消费 Context
function Button() {
const theme = useContext(ThemeContext); // 使用 useContext 获取当前的 theme
return <button>当前按钮的主题是: {theme}</button>;
}

export default App;

说明:

  1. 创建上下文:使用 createContext 创建一个 Context 上下文。
  2. 提供上下文:使用 Provider 提供 theme 值给子组件,所有包裹在 Provider 内部的组件都可以访问到 theme 值。
  3. 消费上下文:使用 useContext 获取 theme 值。优点:当上下文的值发生变化时,所有使用 useContext 的组件会自动重新渲染。可以跨越组件层级传递数据,而无需逐层传递 props

useMemo

useMemoReact 中的一个 Hook,用来 优化性能,它可以记住计算结果并仅在依赖项发生变化时重新计算。useMemo 主要用于避免在每次渲染时进行昂贵的计算,或者减少不必要的重新渲染。

import React, { useState, useMemo } from 'react';

function ExpensiveComponent({ a, b }) {
// 只有当 a 或 b 变化时,才重新计算 expensiveValue
const expensiveValue = useMemo(() => {
console.log('计算昂贵的值...');
return a * b; // 假设这是一个昂贵的计算
}, [a, b]);

return <div>计算结果:{expensiveValue}</div>;
}

function App() {
const [a, setA] = useState(1);
const [b, setB] = useState(2);
const [count, setCount] = useState(0);

return (
<div>
<button onClick={() => setA(a + 1)}>增加 a</button>
<button onClick={() => setB(b + 1)}>增加 b</button>
<button onClick={() => setCount(count + 1)}>增加 count</button>
<ExpensiveComponent a={a} b={b} />
<p>点击次数:{count}</p>
</div>
);
}

export default App;

说明:

  1. ExpensiveComponent 使用了 useMemo 来缓存 a * b 的计算结果,只有当 ab 改变时,才会重新计算这个值。
  2. count 改变时,ExpensiveComponent 并不会重新计算 a * b,因为 count 不在依赖数组中。

React通关秘籍