State Hook
1、16.8以后的新特性,现在可用
2、我们之前的组件都是以类的概念来实现的,里面可以 写函数、属性、state、视图,hook就是让我们在不写类的情况下也能实现效果,hook在类组件丽不生效。 (函数组件 )
3、没有计划移除class,hook和class并存。(可以写类组件,也可以写函数组件)
hook是什么?
????????hook是react的一个特殊的函数,可以引入react的特性,系统的调用函数,useState()后面就带了个括号。
state Hook声明:
????????在函数组件中,我们没有?this ,所以我们不能分配或读取?this.state ?,使用useState,我们可以直接拿到。
const [state, setState] = useState(initialState);
// [属性名,改变属性方法] = useState(属性初始值);
const [state, setState] = useState(function);
// [属性名,改变属性方法] = useState(需要通过计算得到初始值的方法);
函数组件对比类组件:
函数组件对比类组件
函数组件(hook) | 类组件 | 不需要赋值this.state | 需要赋值this.state | 更新数据直接更新 | 需要调用this.stestate | 直接使用数据 | 需要调用this.state.xxx使用数据 |
对比代码示例:?
?类组件:
import React, { Component } from 'react'
export default class classComponent extends Component {
constructor(props){
super(props)
/**
* 初始化:需要赋值
*/
this.state={
counter:100
}
}
handleClick = ()=>{
/**
* 更改数据:需要使用setState({})
*/
this.setState({counter:200})
}
render() {
// const {counter} = this.state
return (
<div>
class组件
{/* 使用:如果不解构,需要调this.state */}
<p>{this.state.counter}</p>
<button onClick={this.handleClick}>修改</button>
</div>
)
}
}
函数组件(hook):
import React,{useState} from "react";
const HookComponent = ()=>{
/**
* 初始化:useState(100) 括号里的就是初始值
*/
const [counter,setCount] =useState(100)
function handleClick (){
/**
* 更改数据:直接调用setCount
*/
setCount(2000) //改数据
}
return (
<div>
<p>hook</p>
{/* 使用:直接访问*/}
<p>{counter}</p>
<button onClick={handleClick}></button>
</div>
)
}
export default HookComponent
Effect Hook
在类组件,我们传统初始化数据的时候会放在生命周期函数里面,比如componentDidMount
import React, { Component } from 'react'
export default class classComponent extends Component {
constructor(props){
this.state={
counter:100
}
}
//初始化数据
componentDidMount(){
console.log(11)
}
render() {
return (
<div>
class组件
</div>
)
}
}
但是函数组件没有生命周期函数,这时候就可以使用useEffect()了。
它可以理解为三个生命周期函数componentDidMount / componentDidUpdate / componentWillUnmount的组合。
可是一个函数怎么代替三个生命周期呢?
- 在数据更新和卸载时都会调用?useEffect()
为什么三个生命要放一起?
- 如果业务很多,由于生命周期各只有一个,所以生命周期函数会写很长。
- 但是useEffect可以写很多个,不限制。相当于把一个生命周期函数里边的业务代码分离了,代码组织能力变强了
- 每一个useEffect都可以处理一个独立的业务
作为?componentDidMount
比如我们需要在网络请求在初始化之后进行赋值
import React,{useState, useEffect} from "react";
import axios from "axios";
const UseEffectComponent = ()=>{
const [data,setData] = useState({})
//网络请求在初始化之后进行赋值,赋值后又会一直调用useEffect,会一直刷新,效率很低
useEffect(()=>{
axios.get('xxx').then(res=>{
setData(res.data)
})
})
return (
<div>
effect应用
<p>{data}</p>
</div>
)
}
export default UseEffectComponent
刚刚说过,在数据更新会调用?useEffect(),那么这里会出现一个问题,请求成功后赋值就相当于更新了数据,又会一直调用useEffect,会一直刷新,这样也不是办法呀!
这个时候,就出现了useEffect()的第二个参数,[ ]。
只要在useEffect()加入[ ]作为第二个参数,就代表componentDidMount?,只执行一次。
作为componentDidUpdate
? ? ? ? 这个第二个参数里是可以写其他属性值的,如果第二个参数里有值,代表这个值改变就会触发这个方法执行 。
? ? ? ? 以下代码实现:count变,打印一次
import React,{useState, useEffect} from "react";
import axios from "axios";
const UseEffectComponent = ()=>{
const [data,setData] = useState({})
const [count,setCount] = useState(0)
useEffect(()=>{
// axios.get('').then(res=>{
// setData(res.data)
// })
console.log(111111)
},[count]) //监听count
function clickHandle(){
setCount(count+1) //点一次按钮,count+1
}
return (
<div>
effect应用
{/* <p>{data}</p> */}
<button onClick={clickHandle}>count+1</button>
{count}
</div>
)
}
export default UseEffectComponent
作为componentWillUnmount
????????这就需要讲讲useEfeect的副作用
useEfeect副作用
? ? ? ? 添加订阅、设置定时器、记录日志都属于副作用,比如设置定时器,我们在useEffect里放了一个定时器,每一秒打印
useEffect(()=>{
timer = setInterval(()=>{console.log(new Date())},1000) //一秒打印一次
})
????????这样在跳转其他页面的时候还是会继续打印,这就是副作用。这些副作用都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性,需要在组件销毁的时候移除掉。
所以需要这在useEfeect后面加上return,清除副作用
useEffect(()=>{
timer = setInterval(()=>{console.log(new Date())},1000) //一秒打印一次
//return清除副作用
return ()=>{
clearInterval(timer)
}
})
完整例子(显示定时器):
import React,{useState,useEffect} from "react"
const TimerApi ={
time:'',
subscribe(cb){
this.timer =setInterval(()=>{
this.time = new Date().toString()
cb(this.time)
})
},
unsubscribe(){
clearInterval(this.timer) //清除副作用
}
}
const EffctClear = ()=>{
const [showTime,setShowTime] = useState('')
useEffect(()=>{
console.log('调用useEffect')
TimerApi.subscribe((time)=>{
setShowTime(time)
})
return ()=>{ //清除副作用
TimerApi.unsubscribe()
}
},[]) //只设置一个定时器
return (
<div>
副作用
{showTime}
</div>
)
}
export default EffctClear
useEffect总结
????????useEffect作为componentDidMount?
useEffect(()=>{ },[])
????????useEffect作为componentDidUpdate?
useEffect(()=>{ },[count])
????????useEffect作为componentWillUnmount
useEffect(()=>{
...
return ()=>{ ... }
},[])
?Hook规则
了解了state Hook和effect Hook,说一下hook规则。
1、只在最顶层使用 Hook
??????? 不要在循环,条件或嵌套函数中调用 Hook, 确保是在React函数的最顶层调用他们。
??????? 比如这样是不行的:
//函数内
fuction test(){
const [data,setData] = useState(0) //错误
useEffect(()=>{}) //错误
}
//判断
if(true){
const [data,setData] = useState(0) //错误
useEffect(()=>{}) //错误
}
//for循环
for(){
const [data,setData] = useState(0) //错误
useEffect(()=>{}) //错误
}
//useEffect()
useEffect(()=>{
const [data,setData] = useState(0) //错误
})
????????需要这样:
const component = ()=>{
const [data,setData] = useState(0) //正确
function test(){}
return ()
}
export default component
?如果我们想要有条件地执行一个 effect,可以将判断放到 Hook 的内部:
useEffect(function persistForm() {
// 👍 将条件判断放置在 effect 中
if (name !== '') {
localStorage.setItem('formData', name);
}
});
2、只在react函数中调用hook
??????? 。。。其他地方也没有hook给你调用
|