定义
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。
useState
通过在函数组件里调用它来给组件添加一些内部 state。useState 会返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。useState 唯一的参数就是初始 state。这个初始 state 参数只有在第一次渲染的时候会被用到。
使用useState可以声明多个state变量。
不使用useState的时候,我们会发现一个最麻烦的问题,就是要去维护this,因为我们知道,每一个class类的this,都是要从父组件继承的,本身是没有this的,所以需要使用constructor去初始化this。
而useState,让我们使用函数编程,我们之前写无状态组件的时候,用的就是函数编程,而使用useState之后,我们可以在函数编程里面使用状态。也就是说:count变成了函数里面的变量,setCount变成了函数里面的函数变量,那么使用的时候直接使用就行了,不再需要使用this
useState接收一个参数,返回的是一个数组,参数表示初始值,数组的第0项就是定义的变量,第1项就是改变变量的方法。
参数可以是基本数据类型,如string、number,也可以是复合数据类型,如array,object等,如下代码
import React, { useState } from 'react'
export default function App() {
const [count, setCount] = useState(0)
const [obj, setObj] = useState({name: "aaa"})
const [arr, setArr] = useState([1,2,3])
return (
<div>
<div>{count}</div>
<button onClick={() => { setCount(count + 1) }}>add</button>
<div>{obj.name}---{obj.age}</div>
<button onClick={() => { setObj({
...obj,
age: 20,
name: "bbb"
}) }}>add</button>
{
arr.map(item => {
return <div>{item}</div>
})
}
<button onClick={() => { setArr([...arr, 4]) }}>add</button>
</div>
)
}
基本数据类型,可以直接修改,而复合数据类型,需要先通过解构运算符,然后再设置成新的复合数据类型。
useEffect
useEffect (副作用函数)是一个 Effect Hook,给函数组件增加了操作副作用(在 React 组件中进行数据获取、订阅或者手动修改 DOM等)的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API,默认情况下,React 会在每次渲染后调用副作用函数 ,包括第一次渲染的时候。
通过使用 Hook,你可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不要把它们拆分到不同的生命周期函数里。
useEffect接收一个函数作为参数,会在页面加载完成的时候以及数据更新的时候,触发执行。
上面实例每次重新渲染都要执行一遍useEffect,如果你在useEffect中使用了useState则会导致无限循环,这样显然是影响性能的。为了处理这个问题,我们可以给useEffect传第二个参数。用第二个参数来告诉react只有当这个参数的值发生改变时,才执行我们传的副作用函数(即第一个参数)。
当我们第二个参数传一个空数组[]时,相当于只在首次渲染的时候执行。
import React,{Component, useState,useEffect} from 'react'
import './App.css';
class App2 extends Component{
state = {
count:0,
size:{
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight,
}
}
onResize=()=>{
this.setState({
size:{
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight,
}
})
}
componentDidMount(){
document.title = this.state.count;
window.addEventListener('resize',this.onResize);
}
componentWillUnmount(){
window.removeEventListener('resize',this.onResize);
}
componentDidUpdate(){
document.title = this.state.count;
}
render(){
const {count,size} = this.state;
return (
<button type="button" onClick={()=>{this.setState({count:count+1})}}>
click{count}
size:{size.width}*{size.height}
</button>
);
}
}
function App(props) {
// const [count,setCount] = useState(()=>{
// //提供一个函数,提高初始效率
// return props.defaultCount||0
// });
const [count,setCount] = useState(0);
const [size,setSize] = useState({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight,
})
const onResize = ()=>{
setSize({
width:document.documentElement.clientWidth,
height:document.documentElement.clientHeight,
})
}
useEffect(()=>{ //我们传递了第二个参数为一个空数组,它表示不监听任何数值,那么useEffect就只会在页面加载完成的时候触发一次,而count更新的时候不会触发。如果想要count更新的时候触发,则在数组内添加参数
console.log('count',count)
},[count])
//useEffect统一在渲染后调用
useEffect(()=>{ //didmount,didupdate
document.title = count;
})
//每次渲染后都运行,加一个【】,绑定的函数只会发生在第一次渲染后,并且在组件卸载之前才会运行绑定函数
//我们都知道定时器在页面销毁的时候,需要清除,我们一般都会在componentUnMount,页面销毁的时候去把定时器清除,那么使用useEffect是如何清除呢?其实很简单,只需要在第一个参数的函数里面,return一个函数,函数里面写清除的方法就行了。这样在页面被卸载的时候,就会把定时器清除
useEffect(()=>{ //didmount,willUnmount
window.addEventListener('resize',onResize);
return ()=>{
window.removeEventListener('resize',onResize);
}
},[])
const onClick=()=>{
console.log('click');
}
useEffect(()=>{
document.querySelector('#size').addEventListener('click',onClick,false)
return ()=>{ //清理状态,获取最新状态
document.querySelector('#size').removeEventListener('click',onClick,false)
}
})
return (
<>
<button type="button" onClick={()=>{setCount(count+1)}}>
click{count}
</button>
{
count%2?
<span id="size">size:{size.width}*{size.height}</span>:
<p id="size">size:{size.width}*{size.height}</p>
}
</>
);
}
export default App;
//在使用useEffext处理title和size的时候,写了两个useEffect,不同的逻辑做不同的事情,这就是关注点分离
// 1、提高了组件复用
// 2、优化了关注点分离
// 两个函数的区别在于第二个参数,空数组,是精髓,只有数组的每一项都不变,所以只有第一次被渲染的时候才会被调用