useState
useState主要用于给组件添加状态变量
基本使用
const [age, setAge] = useState(42);
直接更新
setAge(newState);
函数更新
避免
闭包陷阱
setState(pre => pre + 1);
对象与数组的更新
对象和数组的更新需要创建新的引用
setForm({
...form,
name: e.target.value // 更新这个属性
});
// 错误示例:
// form.name = e.target.value
useReducer
useReducer允许我们使用 action 和 reducer 的方式来组织复杂的状态逻辑,使其变得更加清晰和模块化,弥补了useState的局限性。
基础用法
useReducer接收三个参数:
- reducer 函数:指定如何更新状态的还原函数,它必须是纯函数,以 state 和 dispatch 为参数,并返回下一个状态。
- 初始状态:初始状态的计算值。
useReducer返回两个参数:
- 当前的状态:当前状态。在第一次渲染时,它会被设置为
init(initialArg)或 initialArg(如果没有 init 的情况下)。 - dispatch:调度函数,用于调用 reducer 函数,以更新状态并触发重新渲染。
const [state, dispatch] = useReducer(reducer, initialArg, init?)
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}
dispatch状态更新是异步的
useContext
通过
context实现跨层级共享状态。
简单使用
使用React.createContext创建一个context对象:
const MyContext = React.createContext(defaultValue);
defaultValue是当组件不在任何 Context Provider 内部时的默认值
import { createContext, useContext } from 'react';
const countContext = createContext(111);
function Aaa() {
return <div>
<countContext.Provider value={222}>
<Bbb></Bbb>
</countContext.Provider>
</div>
}
function Bbb() {
return <div><Ccc></Ccc></div>
}
function Ccc() {
const count = useContext(countContext);
return <h2>context 的值为:{count}</h2>
}
export default Aaa;
性能问题
当
Provider的value属性值发生变化时,所有使用了useContext的组件都将重新渲染。
拆分 context
useMemo和useCallback优化value
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}
useRef
- 持久性:
useRef的返回对象在组件的整个生命周期中都是持久的,而不是每次渲染都重新创建。 - 不会触发渲染:当
useState中的状态改变时,组件会重新渲染。而当useRef的.current属性改变时,组件不会重新渲染。
基本使用
// 定义
const inputRef = useRef(null);
// 使用
console.log(inputRef.current)
访问DOM
function TextInput() {
const inputRef = useRef(null);
function focusInput() {
inputRef.current.focus();
}
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus the input</button>
</div>
);
}
保存状态但不触发渲染
function Timer() {
const count = useRef(0);
useEffect(() => {
const intervalId = setInterval(() => {
count.current += 1;
console.log(`Elapsed time: ${count.current} seconds`);
}, 1000);
return () => clearInterval(intervalId);
}, []);
return <div>Check the console to see the elapsed time!</div>;
}
保存上一次的 props 或 state
function DisplayValue({ value }) {
const [prevValue, setPrevValue] = useState(null); // 初始时,没有前一个值
const previousValue = useRef(value);
useEffect(() => {
setPrevValue(currentRef.current);
previousValue.current = value;
}, [value]);
return (
<div>
Current Value: {value} <br />
Previous Value: {prevValue}
</div>
);
}
useEffect
useEffect出现的目的为处理React中的副作用
模拟生命周期
- componentDidMount()
在组件被挂载到 DOM 后调用,通常用于发送网络请求、订阅事件等初始化操作。 使用useEffect: 依赖项为空数组,初次渲染执行
useEffect(() => {
console.log('Component mounted');
// 执行数据获取操作
fetchData();
}, []);
- componentDidUpdate
在组件更新后调用
useEffect(() => {
console.log('Component updated');
// 执行其他副作用操作
document.title = `You clicked ${count} times`;
}, [count]);
使用useEffect: 依赖项为 state,state 变化时执行
- componentWillUnmount 在组件即将从 DOM 中移除时调用,用于清理工作,比如取消订阅、清除定时器等。
useEffect(() => {
return () => {
console.log('Component unmounted');
// 执行清理操作
clearSubscription();
};
}, []);
使用useEffect: 在组件卸载时执行 return,清理副作用
执行时机
effect 函数会在操作 dom 之后异步执行

useLayoutEffect
页面渲染前操作effect

useMemo
通过缓存计算结果,避免在组件渲染时进行不必要的重复计算
const allUsers = useMemo(() => {
let list = [];
for (let i = 1; i <= 500; i++) {
list.push({ id: i, name: `User${i}` });
}
return list;
}, []);
useuseCallback
避免props不必要的变化
const bbbCallback = useCallback(function () {
// xxx
}, []);
useTransition
useTransition是 React 18 中引入的一个 Hook,允许将某些更新标记为“过渡”状态,这样 React 可以优先处理更重要的更新
const [isPending, startTransition] = useTransition()
isPending:是一个布尔值,当过渡状态仍在进行中时,其值为true;否则为false。startTransition:是一个函数,当你希望启动一个新的过渡状态时调用它。
import React, { useState, useEffect, useTransition } from 'react';
const App: React.FC = () => {
const [list, setList] = useState<any[]>([]);
const [isPending, startTransition] = useTransition();
useEffect(() => {
// 使用了并发特性,开启并发更新
startTransition(() => {
setList(new Array(10000).fill(null));
});
}, []);
return (
<>
{list.map((_, i) => (
<div key={i}>{i}</div>
))}
</>
);
};
export default App;
useDeferredValue
延迟某个值的更新,使高优先级的任务可以先行完成。
const deferredValue = useDeferredValue(someValue);
其中someValue是你想要延迟的值,它可以是任何类型。
deferredValue的渲染有两种情况:
- 在初始渲染时,
deferredValue的值将与someValue的值相同。 - 在UI更新期间,因为
deferredValue的优先级较低,即使并发模式下deferredValue已在后台更新,React也会先使用旧值渲染,当其它高优先级的状态更新完成,才会把deferredValue新值渲染出来。
import { useState, useDeferredValue, memo } from 'react';
export default function App() {
const [text, setText] = useState('');
const deferredText = useDeferredValue(text);
return (
<>
{/* 输入框的值直接与 text 绑定,所以输入框会实时显示用户的输入 */}
<input value={text} onChange={e => setText(e.target.value)} />
{/* SlowList 组件接受 deferredText 作为属性,渲染优先级会被降低 */}
{/* 在 UI 真正更新前,如果 deferredText 被更新多次,也只会保留最后一次的结果 */}
<SlowList text={deferredText} />
</>
);
}
useInsertionEffect
为CSS-in-JS赋能
useEffect注入 :重复布局
useLayoutEffect注入:阻塞渲染
useInsertionEffect优点:
- 动态性:允许在运行时动态地注入样式,这使得基于组件的状态、道具或上下文的样式变化变得容易。
- 及时注入:保证了在任何布局效果触发之前插入样式,减少了样式的重复计算和布局抖动。
import { useInsertionEffect } from 'react';
function useDynamicStyle(styleObj) {
const cssString = convertStyleObjToCSS(styleObj); // 将样式对象转换为 CSS 字符串的辅助函数
useInsertionEffect(() => {
const styleElement = document.createElement('style');
styleElement.innerHTML = cssString;
document.head.appendChild(styleElement);
return () => {
document.head.removeChild(styleElement);
};
}, [cssString]);
}
在useInsertionEffect里面,我们可以动态注入<style>。
forwardRef
import { useRef } from 'react';
import { useEffect } from 'react';
import React from 'react';
const Guang= (props, ref) => {
return <div>
<input ref={ref}></input>
</div>
const WrapedGuang = React.forwardRef(Guang);
function App() {
const ref = useRef<HTMLInputElement>(null);
useEffect(()=> {
console.log('ref', ref.current)
ref.current?.focus()
}, []);
return (
<div className="App">
<WrapedGuang ref={ref}/>
</div>
);
}
export default App;

useImperativeHanlde
自定义暴露给父组件的实例方法或属性

useSyncExternalStore
useSyncExternalStore解决的是并发模式下数据管理的问题。
useId
const id = useId()
useDebugValue
useDebugValue是一个专为开发者调试自定义 hook 而设计的 React hook。
use
简化Promise用法
简化 Suspense 的使用
const [post, setPost] = useState(null);
useEffect(() => {
fetchPost(postId).then(data => setPost(data));
}, [postId]);
const postPromise = fetchPost(postId);
const post = use(postPromise);