useState
useState用于在函数组件中添加状态。它接受一个初始值作为参数,并返回一个包含当前状态值和更新状态值的数组。
使用方法
state是当前状态的值,setState是用于更新状态的函数,initialValue是初始值。
scss
const [state, setState] = useState(initialValue);
在组件中可以通过state来访问当前状态的值,通过setState来更新状态的值。
scss
// 读取状态值
console.log(state);
// 更新状态值
setState(newValue);
useState可以多次使用,用于声明多个状态变量。
scss
const [state1, setState1] = useState(initialValue1);
const [state2, setState2] = useState(initialValue2);
更新状态的函数可以接受一个新值,也可以接受一个函数。
scss
const [num, setNum] = useState(0);
setNum(1);
setNum(old=>old+1);
惰性初始化 state
initialState 参数只会在组件的初始化渲染中起作用,后续渲染时会被忽略。
如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用。
这样可以避免在每次渲染时都执行昂贵的初始化计算。
ini
const [state, setState] = useState(() => {
const initialValue = someExpensiveComputation();
return initialValue;
});
注意事项
- useState是按顺序声明的,所以在组件中的顺序很重要。
- 更新函数它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并,而是直接替换
- 在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同
useCallback+useMemo
useCallback
useCallback用于缓存一个函数,以便在依赖项不变的情况下避免函数的重新创建。它接受一个回调函数和一个依赖项数组,并返回一个缓存的回调函数。
使用方法
ini
const memoizedCallback = useCallback(callback, dependencies);
这里的callback是要缓存的回调函数,dependencies是一个依赖项数组,当数组中的任何一个值发生变化时,缓存的回调函数会重新创建。
例如,当你将回调函数作为props传递给子组件时,可以使用useCallback来确保子组件不会在父组件重新渲染时重新创建回调函数。
javascript
import React, { useCallback } from 'react';
function MyComponent() {
const handleClick = useCallback(() => {
// 处理点击事件的逻辑
}, []); // 依赖数组为空,表示回调函数不依赖任何值
return (
<button onClick={handleClick}>Click me</button>
);
}
在上面的例子中,handleClick
函数会在组件第一次渲染时创建,并且在后续的重新渲染中复用。如果依赖数组不为空,那么只有当依赖项发生变化时,handleClick
函数才会重新创建。
一般来说,useCallback
适用于以下场景:
- 当你将回调函数作为参数传递给子组件时,可以使用
useCallback
来确保子组件不会频繁重新渲染。 - 在使用
memo
或shouldComponentUpdate
进行组件性能优化时,可以用useCallback
来缓存回调函数。
需要注意的是,由于useCallback
使用了浅比较来确定回调函数是否需要重新创建,所以如果回调函数内部引用了依赖项(如变量或状态),需要确保依赖项是稳定的,避免出现意外的行为
useMemo
useMemo用于缓存一个值,以便在依赖项不变的情况下避免值的重新计算。它接受一个计算函数和一个依赖项数组,并返回一个缓存的值。
使用方法
ini
const memoizedValue = useMemo(callback, dependencies);
这里的callback是计算函数,dependencies是一个依赖项数组,当数组中的任何一个值发生变化时,缓存的值会重新计算。
javascript
import React, { useMemo } from 'react';
function MyComponent({ a, b }) {
const result = useMemo(() => {
// 根据依赖项a和b进行计算
return a + b;
}, [a, b]); // 依赖项为[a, b]
return (
<div>{result}</div>
);
}
在上面的例子中,result
变量会在组件第一次渲染时计算,并在后续的重新渲染中复用,除非依赖项a
或b
发生变化。如果依赖项发生变化,result
将会重新计算。
useMemo
适用于以下场景:
- 当一些计算比较昂贵,但依赖项不经常变化时,可以使用
useMemo
缓存计算结果,避免重复计算。 - 当在渲染期间需要进行数据转换或映射时,可以使用
useMemo
缓存转换结果。 - 在使用
memo
或shouldComponentUpdate
进行组件性能优化时,可以用useMemo
缓存具有相同结果的值。
需要注意的是,由于useMemo
使用了浅比较来确定依赖项是否发生变化,所以如果依赖项是引用类型(如对象或数组),需要确保依赖项的引用地址不变,以便正确触发重新计算。
当在渲染期间需要进行数据转换或映射时,可以使用useMemo
缓存转换结果,以避免重复计算。以下是一个简单的例子,展示了如何使用useMemo
来进行数据转换:
javascript
import React, { useMemo } from 'react';
function MyComponent({ data }) {
const transformedData = useMemo(() => {
// 进行数据转换,例如将每个元素的值增加1
return data.map(item => item + 1);
}, [data]); // 依赖项为[data]
return (
<div>
{transformedData.map(item => (
<span key={item}>{item}</span>
))}
</div>
);
}
在上面的例子中,data
是一个数组,我们希望对每个元素的值进行加一的操作。通过使用useMemo
,我们可以将数据转换的逻辑定义为计算函数,并将data
作为依赖项。
当data
发生变化时,transformedData
会重新计算。但在同一个渲染周期内,如果data
没有发生变化,那么transformedData
会复用上一次计算的结果,从而避免重复的数据转换操作。
这种方式可以提高性能,特别是当data
数组较大或者转换操作比较复杂时。通过缓存计算结果,可以避免每次重新渲染都进行昂贵的数据转换操作,从而提升组件的渲染性能。
使用useCallback+useMemo+React.memo进行性能优化
typescript
import React,{useState,memo,useMemo,useCallback} from 'react';
function SubCounter({onClick,data}:{onClick:()=>void,data:{number:number}}){
console.log('SubCounter render');
return (
<button onClick={onClick}>{data.number}</button>
)
}
const NewSubCounter = memo(SubCounter);
export default function Counter6(){
console.log('Counter render');
const [name,setName]= useState('计数器');
const [number,setNumber] = useState(0);
const data = {number}
const addClick = ()=>{
setNumber(number+1);
}
// const data =useMemo(()=>({number}),[number])
// const addClick =useCallback(()=>{
// setNumber(number+1);
// },[number]);
return (
<>
<input type="text" value={name} onChange={(e)=>setName(e.target.value)}/>
<NewSubCounter data={data} onClick={addClick}/>
</>
)
}
函数组件使用 React.memo
,将函数组件传递给 memo
之后,就会返回一个新的组件,新组件的功能:如果接受到的属性不变,则不重新渲染函数;
上面的代码中的 const NewSubCounter = memo(SubCounter);
将SubCounter组件做处理。 但是它接收的参数data和addClick在父组件重新渲染时一起改变,所以当你input输入时能看到两个打印SubCounter render
和Counter render
,使用useMemo
,useCallback
处理了数据和函数后,input输入出现一个打印,点击按钮出现两个打印。