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,通常用于处理复杂的状态逻辑,尤其是当状态更新逻辑比较复杂时,或者有多个状态依赖于某些条件时,useReducer
比 useState
更加适用。它是基于 Reducer 模式实现的,类似于 Redux 中的机制。
const [state, dispatch] = useReducer(reducer, initialState)
state
: 当前的状态。dispatch
: 用来发送动作(action)的函数,触发状态更新。reducer
: 是一个函数,定义了如何根据传入的 action 来更新状态。initialState
: 初始化的状态。
reducer
是一个函数,接收两个参数:
state
: 当前的状态。action
: 动作,通常是一个对象,包含type
和payload
等属性。
例子:
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
属性是可变的,并且不会触发重新渲染。initialValue
是ref
的初始值。
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;
说明:
- 父组件通过
ref
直接访问子组件的 DOM 元素或组件实例。 - 子组件通过
forwardRef
将ref
传递给父组件。 - 父组件通过
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;
说明:
MyInput
是一个子组件,使用useImperativeHandle
暴露了focus、reset 和 getValue
方法。- 父组件通过
ref
直接访问子组件的focus、reset 和 getValue
方法。
useImperativeHandle
通常与 forwardRef
一起使用,因为它需要访问由父组件传递下来的 ref
。forwardRef
用来转发 ref
,而 useImperativeHandle
用来自定义这个 ref
所暴露的实例方法。
useContext
useContext
是 React
中的一个 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;
说明:
- 创建上下文:使用
createContext
创建一个Context
上下文。 - 提供上下文 :使用
Provider
提供theme
值给子组件,所有包裹在Provider
内部的组件都可以访问到theme
值。 - 消费上下文:使用
useContext
获取theme
值。优点:当上下文的值发生变化时,所有使用useContext
的组件会自动重新渲染。可以跨越组件层级传递数据,而无需逐层传递props
。
useMemo
useMemo
是 React
中的一个 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;
说明:
ExpensiveComponent
使用了useMemo
来缓存a * b
的计算结果,只有当a
或b
改变时,才会重新计算这个值。- 当
count
改变时,ExpensiveComponent
并不会重新计算a * b
,因为count
不在依赖数组中。