useState
- 作用:
-
让函数组件像类组件一样拥有自己的状态
jsximport React, { useState } from 'react'; function Example() { const [count, setCount] = useState(() => { // 模拟复杂的计算 const initialValue = 2 * 3; return initialValue; }); setCount((oldValue)=>{ return oldValue+1; }) return ( <div> <p>初始值为: {count}</p> </div> ); } export default Example;
-
参数说明:
useState
:接收一个参数,作为状态的初始值,参数有两种形式,静态和动态- 静态值:直接传入一个具体的值作为状态的初始值,如字符串、数字等
- 动态值:传入一个计算函数,计算函数的返回值会作为状态的初始值
setCount
:接收一个参数,用来更新当前状态,参数也有两种形式,静态和动态- 静态值:传入一个值,用来替换原状态的值
- 动态值:传入一个函数,函数接收一个参数(旧的状态值),函数的返回值用来替换原状态值
-
useEffect
-
写法1:useEffect(callback)
- 组件第一次渲染完毕之后,执行callback,等价于componentDidMount
- 在每次组件更新完毕之后,也会执行callback,等价于componentDidUpdate
-
写法2:useEffect(callback, [])
- 只有在第一次渲染完毕之后,才会执行callback,以后每次更新完毕,不再执行
- 类似于componentDidMount
-
写法3:useEffect(callback, [依赖状态1,依赖状态2,...])
- 第一次渲染完毕之后,会执行callback
- 当某一个或者多个依赖发生变化时,callback会执行
- 如果组件更新,但是依赖没有变化,callback不会执行
-
写法4:
jsxlet [state,setSate] = useState(0) useEffect(()=>{ //1、返回的内部函数,会在组件释放的时候执行 //2、最常见的用法就是在这个函数里清除副作用 return ()=>{ console.log(state);//这里打印的state是组件更新之前的状态值 } })
- 与
写法1
的执行一样,但是内部函数的执行时机见上面代码注释
- 与
useLayoutEffect与useEffect的区别
-
useLayoutEffect是同步执行的,会在阻塞浏览器的渲染,会在 DOM 更新完成后,但是浏览器绘制之前执行。可以做:获取 DOM 的宽高、修改样式等操作;以防止页面出现闪烁、抖动,提高用户体验。由于其会阻塞页面的绘制,所以需要谨慎使用。
- 流程:
DOM 更新 → useLayoutEffect回调执行 → 浏览器绘制(用户可见)
。
- 流程:
-
useEffect是异步执行的,不会阻塞浏览器的渲染,用户可以先看到页面的更新,然后
useEffect
的回调函数再开始执行。所有它比较适合处理一些不影响页面渲染的副作用,比如请求数据等。- 流程:
DOM 更新 → 浏览器绘制(用户可见)→ useEffect 回调执行
。
- 流程:
useMemo
用来缓存复杂计算的结果,只要依赖项没有发生变化,结果就不会重新计算,可以提高性能。
jsx
const memoizedValue = useMemo(() => {
// 进行一些复杂的计算
return computedValue;
}, [dependency1, dependency2, ...]);
- 参数:
- 参数1:传入回调函数,组件初次渲染时调用,当依赖项发生变化时重新调用,返回计算的结果,会作为useMemo的返回结果保存起来
- 参数2:传入一个数组,数组元素是依赖项,当任意一个依赖项发生改变时,useMemo都会重新调用回调函数,计算结果;如果传入的是一个空数组
[]
,则useMemo的回调函数只会在组件初次渲染时调用一次;如果不传递第二个参数,组件每次渲染都会执行一次回调函数,也就失去了使用useMemo来优化性能的意义。
- 返回值:useMemo返回的是回调函数的返回值,并将这个值缓存起来。
useMemo与useEffect的区别
- 参数
- useMemo与useEffect的参数一样
- 返回值
- useMemo返回传入回调函数的返回值,并把这个值缓存起来,避免不必要的重复计算
- useEffect可以返回一个清理函数,在组件卸载或下一次副作用执行之前调用,用于清除副作用(清除定时器、取消事件订阅等)
- 执行时机
- useEffect渲染之后异步执行,也就是说组件渲染完成,页面更新到屏幕上,然后useEffect的回调函数才会执行;为了避免副作用阻塞页面的渲染过程。
- useMemo在渲染期间同步执行,当组件渲染时,useMemo会立即检查依赖项是否发生变化,如果没有发生变化,就直接返回之前的结果,如果发生了变化,就重新计算结果并且更新缓存。
- 应用场景
- useEffect用来处理副作用操作,比如获取数据、事件订阅、定时器、修改DOM等
- useMemo
- 用来处理复杂计算,并且缓存结果,避免重复计算,提高性能;
- 执行在 DOM 更新之前,属于 "渲染中" 的步骤;
- 会阻塞渲染流程,若计算耗时过长,会导致页面卡顿;
- 流程:
触发渲染 → 执行 useMemo 回调(计算值)→ 生成虚拟 DOM → 完成渲染
useCallback
函数组件每次重新渲染时,其内部定义的函数都会被重新创建;如果这个函数被传递给子组件,可能会导致子组件不必要的更新,即使函数的逻辑并没有发生变化;useCallbaack可以帮我们记住函数的引用,只有当依赖项发生变化时,才会重新创建函数,从而避免子组件不必要的渲染。
jsx
import React, { useState, useCallback } from 'react';
// 子组件
const ChildComponent = React.memo(({ onClick }) => {
return <button onClick={onClick}>点击我</button>;
});
function ParentComponent() {
const [count, setCount] = useState(0);
// 使用 useCallback 记忆化函数
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
<div>
<p>计数: {count}</p>
<ChildComponent onClick={handleClick} />
</div>
);
}
export default ParentComponent;
useCallback与useMemo对比
- 参数
- useCallback与useMemo的参数一样
- 作用
- useCallback用来记忆函数的引用
- useMemo用来缓存计算结果
- 都可以用来优化性能
useContext
react应用中的数据一般是通过props来传递的,但是如果组件层级非常深的情况下,使用起来比较繁琐,useContext结合React.createContext,可以在任意下级组件中共享数据,不需要再层层传递。
- 创建context
jsx
import React from 'react';
// 创建一个上下文对象,初始值为 null
const MyContext = React.createContext(null);
export default MyContext;
-
提供上下文数据
jsximport React from 'react'; import MyContext from './MyContext'; const ParentComponent = () => { const sharedData = { message: 'Hello from context', count: 10 }; return ( <MyContext.Provider value={sharedData}> {/* 子组件 */} <ChildComponent /> </MyContext.Provider> ); }; export default ParentComponent;
-
子组件中使用useContext
jsximport React, { useContext } from 'react'; import MyContext from './MyContext'; const ChildComponent = () => { // 使用 useContext 获取上下文数据 const contextData = useContext(MyContext); return ( <div> <p>{contextData.message}</p> <p>Count: {contextData.count}</p> </div> ); }; export default ChildComponent;
useReducer
它是 useState
的替代方案,适用于管理复杂的状态逻辑;
jsx
const [state, dispatch] = useReducer(reducer, initialArg, init);
-
参数:
reducer
:是一个纯函数,接收两个参数:当前状态state
和一个动作action
,并返回一个新的状态。其形式通常为(state, action) => newState
。initialArg
:初始状态值,或者用于初始化状态的参数。init
(可选):是一个初始化函数,用于惰性初始化状态。如果提供了init
函数,初始状态将是init(initialArg)
的返回值。
-
返回值
state
:当前状态值dispatch
:一个函数,用于触发状态更新,接收一个动作action
作为参数。
-
使用
jsximport React, { useReducer } from 'react'; // 定义 reducer 函数 const counterReducer = (state, action) => { switch (action.type) { case 'increment': return { count: state.count + 1 }; case 'decrement': return { count: state.count - 1 }; default: return state; } }; function Counter() { // 使用 useReducer 初始化状态 const [state, dispatch] = useReducer(counterReducer, { count: 0 }); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: 'increment' })}>Increment</button> <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button> </div> ); } export default Counter;
useRef
-
保存可变值 :它可以创建一个可变的对象,在组件的整个生命周期内保持不变。每次组件重新渲染时,
useRef
返回的对象都是同一个,你可以通过修改该对象的current
属性来存储和读取数据,并且不会触发组件的重新渲染。 -
获取 DOM 节点 :可以通过
useRef
来引用 DOM 元素,从而可以在代码中直接操作该 DOM 元素,例如获取元素的尺寸、滚动位置等。jsximport React, { useRef } from 'react'; function Example() { const inputRef = useRef(null); const focusInput = () => { // 通过 ref 获取 DOM 节点并调用其方法 inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" /> <button onClick={focusInput}>聚焦输入框</button> </div> ); } export default Example;
forwardRef与useImperativeHandle
- forwardRef : 是 React 提供的一个高阶组件,用于在组件之间转发
ref
。在 React 中,ref
是一种访问 DOM 节点或组件实例的方式,但默认情况下,组件不能直接将ref
传递给子组件。forwardRef
可以打破这种限制,允许父组件将ref
传递给子组件,从而让父组件可以访问子组件的 DOM 节点或调用子组件的方法。 - useImperativeHandle :
useImperativeHandle
通常与forwardRef
一起使用,用于自定义通过ref
暴露给父组件的实例值。默认情况下,当使用ref
引用一个组件时,父组件可以访问该组件的所有实例属性和方法,但有时候我们可能只想暴露部分特定的属性或方法,这时就可以使用useImperativeHandle
来实现。
jsx
import { forwardRef, useImperativeHandle, useRef, useEffect } from 'react'
const MyInput = forwardRef((props,ref) => {
const inputRef = useRef(null)
// 自定义子组件通过ref暴露给父组件的方法
useImperativeHandle(ref,()=>{
return {
//暴露方法
focus:()=>{
inputRef.current.focus();
},
clear:()=>{
inputRef.current.value = '';
},
getValue:()=> inputRef.current.value
}
},[])//依赖为空数组,只初始化一次
return <input {...props} ref={inputRef}></input>
});
function Prarent(){
const myInputeRef = useRef(null)
//挂载完成后自动聚焦
useEffect(()=>{
if(myInputeRef.current){
myInputeRef.current.focus()
}
},[])
return <MyInput ref={myInputeRef} />
}
React.memo
是React提高的一个高阶组件,作用是缓存组件的渲染结果,通过浅比较props,当props没有变化时,直接返回缓存的渲染结果,主要用于性能优化,但是也要避免过度使用React.memo,因为其本身也有一定的性能开销
语法 :const MemoizedComponent = react.memo(component,fn(prevProps,curProps))
- 参数一(必传):传递一个需要缓存渲染结果的组件
- 参数二(可选):传递一个自定义的比较函数,函数接收两个参数,参数一前一次的props,参数二当前的props,返回一个
Boolean
值- 值为
true
时:表示props没有变化,复用缓存结果 - 值为
false
时:表示props变化了,需要重新渲染
- 值为
jsx
import { memo } from 'react';
// 接收两个参数:前一次的 props 和当前的 props
// 返回 true:表示 props 未变化,复用缓存
// 返回 false:表示 props 变化,需要重渲染
const arePropsEqual = (prevProps, nextProps) => {
// 自定义比较逻辑:比较对象的内容而非引用
return (
prevProps.user.name === nextProps.user.name &&
prevProps.user.age === nextProps.user.age
);
};
// 组件接收一个对象类型的 props
const UserInfo = ({ user }) => {
console.log('UserInfo 渲染了');
return (
<div>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
</div>
);
};
// 使用自定义比较函数的缓存组件
const MemoizedUserInfo = memo(UserInfo, arePropsEqual);
// 父组件
const Parent = () => {
// 即使创建新对象,但内容不变,MemoizedUserInfo 也不会重渲染
const [user, setUser] = useState({ name: 'Alice', age: 20 });
const handleClick = () => {
// 创建新对象(引用变化),但内容与之前相同
setUser({ name: 'Alice', age: 20 });
};
return (
<div>
<button onClick={handleClick}>更新用户(内容不变)</button>
<MemoizedUserInfo user={user} />
</div>
);
};