useState
用法:const [state, setState] = useState(initialState);
initialState:state的初始值
setState:用于更新state的值,它接收一个新的state值并将组件的一次重新渲染加入队列,在后续渲染中useState返回的第一个值将始终是更新后最新的state
1、如果新的state需要通过使用先前的state计算得出,那么可以将函数传递给setState。该函数将接收先前的state,并返回一个更新后的值
function Counter({initialCount}) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count}
<button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
复制代码
2、如果初始state需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的state,此函数只在初始渲染时被调用
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
复制代码
useEffect
1、在函数体内执行有其他副作用的操作都是不被允许的,因为会产生莫名其妙的bug并且会破坏UI的一致性。
2、默认情况下,effect将在每轮渲染结束后执行,这样的话,一旦effect的依赖发生变化,它就会被重新创建。但你也可以选择让它在只有某些值改变的时候才执行。
3、虽然useEffect会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React总会先清除上一轮渲染的effect。
4、useEffect最后,不加[]就表示每一次渲染都执行,用来替代willUpdate等每次渲染都会执行的生命函数
useEffect(()=>{
const users = 每次都获取全国人民的信息()
})
复制代码
5、useEffect最后,加了[]就表示只第一次执行,类似于componentDidMount
useEffect(()=>{
const users = 获取全国人民的信息()
},[])
复制代码
6、useEffect最后,加[],并且[]里面加的字段就表示,这个字段更改了,我这个effect才执行
useEffect(() => {
const users = (name改变了我才获取全国人民的信息())
},[name])
复制代码
6、我们不需要在每次组件更新时都创建新的订阅,而是仅需要在source prop改变时重新创建。要实现这一点,可以给useEffect传递第二个参数,它是effect所依赖的值数组。在effect的return里面可以做取消订阅的事,类似于willUnMount
useEffect(
() => {
const subscription = props.source.subscribe();
return () => {
subscription.unsubscribe();
};
},[props.source]); //此时,只有当props.source改变后才会重新创建订阅
复制代码
注意:如果你要使用此优化方式,请确保数组中包含了所有外部作用域中会发生变化且在effect中使用的变量,否则你的代码会引用到先前渲染中的旧变量。请记得React会等待浏览器完成画面渲染之后才会延迟调用useEffect。
7、规则细节
1)useEffect 里面使用到的state的值, 固定在了useEffect内部, 不会被改变,除非useEffect刷新,重新固定state的值
const [count, setCount] = useState(0)
useEffect(() => {
console.log('use effect...',count)
const timer = setInterval(() => {
console.log('timer...count:', count)
setCount(count + 1)
}, 1000)
return ()=> clearInterval(timer)
},[])
复制代码
2)useEffect不能被判断包裹
const [count, setCount] = useState(0)
if(2 < 5){
useEffect(() => {
console.log('use effect...',count)
const timer = setInterval(() => setCount(count +1), 1000)
return ()=> clearInterval(timer)
})
}
复制代码
useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
复制代码
1、把“创建”函数和依赖项数组作为参数传入useMemo,它仅会在某个依赖项改变时才重新计算memoized值,这种优化有助于避免在每次渲染时都进行高开销的计算。
2、传入useMemo的函数会在渲染期间执行,请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于useEffect的适用范畴,而不是useMemo。
3、先根据[name]里面的name值判断一下,因为useMemo 作为一个有着暂存能力的,暂存了上一次的name结果。对比上一次的name,我们发现name值没有改变,那么这次data就不重新赋值成新的对象了!
const data = useMemo(()=>{
return {
name
}
},[name])
复制代码
4、React.memo和useMemo的区别:如果函数组件被 React.memo 包裹,且其实现中拥有useState或useContext的Hook,当context发生变化时,它仍会重新渲染。memo是浅比较,对象只比较内存地址,只要你内存地址没变,管你对象里面的值千变万化都不会触发render,于是就需要使用到useMemo。useMemo解决值的缓存。
useRef
useEffect里面的state的值,是固定的,这个是有办法解决的,就是用useRef,可以理解成useRef的一个作用:
1、就是相当于全局作用域,一处被修改,其他地方全更新…
const [count, setCount] = useState(0)
const countRef = useRef(0)
useEffect(() => {
console.log('use effect...',count)
const timer = setInterval(() => {
console.log('timer...count:', countRef.current)
setCount(++countRef.current)
}, 1000)
return ()=> clearInterval(timer)
},[])
复制代码
2、普遍操作,用来操作dom
const refContainer = useRef(initialValue);
复制代码
useRef 返回一个可变的ref对象,其.current属性被初始化为传入的参数(initialValue)。返回的ref对象在组件的整个生命周期内保持不变。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
复制代码
本质上,useRef就像是可以在其.current属性中保存一个可变值的“盒子”,useRef()比ref属性更有用。它可以很方便地保存任何可变值,useRef()和自建一个{current: …}对象的唯一区别是,useRef会在每次渲染时返回同一个ref对象。
useCallback
1、useMemo是缓存值的,而useCallback是缓存函数
2、没有依赖,添加空的依赖,就是空数组
const onChange = useCallback((e)=>{
setText(e.target.value)
},[])
复制代码