创建日期:2020年3月12日
题记:本篇,不过多的去讲原理,只告诉你如何用,让你快速上手。需要知道更详细的,笔者之后再慢慢更新吧,当然,推荐你看英文版官网。中文版的有缩略,读起来也不顺畅。如果,你只想知道如何用,这篇文,足够了。
官网
中文文档
一.useState
const [state, setState] = useState(initialState);
1.initialState是初始值,只在第一次运行时候会被调用
2.每次调用setState时,都会重新渲染画面。这个过程是异步的,相当于把赋值以及re-render的动作,加入到队列中。(如果你还理解不好,第二部分,讲useEffect的时候,下面的“题外话”你可以仔细读读,按着里面的错误做法,尝试一次,就会记忆深刻了。)
3.与类组件中的 setState 方法不同,useState 不会自动合并更新对象。即,你每调用一次setState,就会重新渲染一次界面。
官方解决方案一: 你可以通过将函数更新器表单与对象扩展语法结合来复制此行为:
setState(prevState => {
// Object.assign 也是可行的
return {...prevState, ...updatedValues};
});
官方解决方案二:用 useReducer ,它更适合管理包含多个子值的 state(状态) 对象。具体用法待调查
个人方案: 这个例子其实不好,如果这些值都来源于网络请求,一次性更新values的所有状态,又只执行一次setState这个函数,你就会发现其中的妙处。有时间重新做个demo,加上用定时器整体赋值values,模仿网络请求
import React, {useState} from "react";
import {Box, TextField} from "@material-ui/core";
const UseStateDemo = () =>{
const originValue = {
k1: '',
k2: '',
k3: '',
k4: '',
k5: '',
}
const [values, setValues] = useState(originValue)
const handleChange = (key: string) => (event: any) => {
setValues({
...values,
[key]: event.target.value,
})
}
//const handleChange = event =>{ setValue(event.target.value) }
console.log("这里运行了")
return(
<Box>
<TextField
value={values.k1}
onChange={handleChange('k1')}
/>
{/*......省略*/}
</Box>
)
}
export { UseStateDemo }
4.延迟初始化
initialState 参数是初始渲染期间使用的状态。 在随后的渲染中,它会被忽略了。 如果初始状态是高开销的计算结果,则可以改为提供函数,该函数仅在初始渲染时执行:
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation(props);
return initialState;
});
二.useEffect
useEffect(didUpdate,[依赖数组]);
1.其内部是一个函数和依赖数组,依赖数组可以省略。
2.它类似于类函数中的生命周期函数与 componentDidMount 和 componentDidUpdate。每次画面渲染完成后执行。当省略依赖数组的时候,每次渲染,该函数都会被执行,当设置依赖数组时,仅当依赖数组的值被改变时,才会被执行。
3.清理effect,当useEffect里面加了订阅或者计时器等,需要在组件卸载时,进行清理时,useEffect需要返回一个清理函数:
useEffect(() => {
const subscription = props.source.subscribe();
return () => {
// Clean up the subscription
subscription.unsubscribe();
};
});
Demo1:我们来举个例子,说明依赖数组的作用,下面是完整的代码:
import React, {useEffect, useState} from "react";
import {Box, Button, TextField} from "@material-ui/core";
const UseEffectDemo = () =>{
const originValue = {
k1: '',
k2: '',
k3: '',
}
const [values, setValues] = useState(originValue)
const [buttonDisabled, setButtonDisabled] = useState(true)
const handleChange = (key: string) => (event: any) => {
setValues({
...values,
[key]: event.target.value,
})
}
const handleClick = () => {
}
console.log("🔹【UseEffectDemo】运行了")
useEffect(
() => {
console.log("🔴【useEffect】运行了")
setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
},[values]
)
//TODO:如何让box内部纵向布局呢?
return(
<Box display="flex" alignItems="flex-start">
<TextField
value={values.k1}
onChange={handleChange('k1')}
/>
<TextField
value={values.k2}
onChange={handleChange('k2')}
/>
<TextField
value={values.k3}
onChange={handleChange('k3')}
/>
<Button disabled={buttonDisabled} onClick={handleClick}>
SAVE
</Button>
</Box>
)
}
export { UseEffectDemo }
(1)正常运行下的输入台:
![]() 第一次启动时
|
![]() 每次输入一个字母后,打印出的
|
|---|
(2)我们删除依赖数组:
useEffect(
() => {
console.log("🔴【useEffect】运行了")
setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
})
![]() 第一次启动时
|
![]() 每次输入一个字母后,打印出的
|
|---|
(2)我们将useEffect拆分成三个,分别依赖k1,k2,k3的值
useEffect(
() => {
console.log("🔴【useEffect---k1】运行了")
setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
},[values.k1])
useEffect(
() => {
console.log("🔴【useEffect---k2】运行了")
setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
},[values.k2])
useEffect(
() => {
console.log("🔴【useEffect---k3】运行了")
setButtonDisabled(!values.k1 || !values.k2 || !values.k3 || false)
},[values.k3])
![]() 第一次启动时
|
![]() 在k1的TextFiled输入一个字母时
|
|---|
我们可以看到,在k1的textFiled里输入值时,仅有依赖value.k1的的useEffect运行了,其余两个同理。也就是说,当依赖数组里的值发生变化时,useEffect才会被执行。
题外话:初学者,有很多人都喜欢在TextFiled的onChange函数里去判断按钮的活性,但是,组件的setState是异步的,不会立即更新,只是将更新放在队列中。在onChage里去判断按钮活性的时候,用的并非是更新后的状态值,而是textFiled的更新前的状态值。所以,会出现bug,比如,你输入值后,又全部删除,因为判断活性用的是更新前的状态,按钮本该变为非活性的,但是,仍保持着活性状态,这是很多初学者容易犯的错误,当然,笔者当年没少被这地方坑过,特别写惯了Java这一类面向对象的开发语言后,初看react,也不大认真,网上各资料很少有强调setState的异步特性,因此很难理解这个bug是如何产生的。
另外,在官网上看到一句对setState函数的描述:
The function will receive the previous value, and return an updated value。
就刚才说的问题,有时间可以尝试一下,在TextFiled的onChange中,用setState返回的值代替state来进行判断,是否就可以了呢?但是,还是更推荐用useEffect来实现。
三.useContext
用于两个组件共享状态。
使用:https://blog.csdn.net/weixin_43606158/article/details/100750602
https://reactjs.org/docs/context.html




