react-hooks 学习

178 阅读4分钟

useState

我们知道函数组件是不能拥有自己的状态组件的,新的Api useState,让我们的函数组件也拥有自己的状态。比如:

function Hp (props) {
    const [count, setCount] = useState(props.index);
    let someIndex = 0
    return <div ref={val}>
        <Button 
        type='link'
        onClick={()=>{
        someIndex++
            setIndex(index+1)
        }}
        >修改我是index</Button> 
        <p> 我是hooks index {index}</p>
        </div>
}

这是一个组件,我们在组件中使用了useState,在第一次组件渲染时,初始化count这个useHooks,并赋予初始值props.index,当我们的props或者count发生变化时,这个组件都会重新渲染。当我们的组件被注销掉,并重新渲染时,count的状态就会被重置。

让我们想想,someIndex发生变化时,组件和子组件会发生渲染更新吗?答案是不会,而且每当组件更新时,这个值都会重新初始化。至于useState底层是如何实现,如何记录的我们暂时不提(没看源码,不知道)

useEffect

这个比上一个稍微复杂一点,我们看看react中文文档上是如何描述的呢,

useEffect 做了什么? 通过使用这个 Hook,你可以告诉 React 组件需要在渲染后执行某些操作。React 会保存你传递的函数(我们将它称之为 “effect”),并且在执行 DOM 更新之后调用它。在这个 effect 中,我们设置了 document 的 title 属性,不过我们也可以执行数据获取或调用其他命令式的 API。

为什么在组件内部调用 useEffect? 将 useEffect 放在组件内部让我们可以在 effect 中直接访问 count state 变量(或其他 props)。我们不需要特殊的 API 来读取它 —— 它已经保存在函数作用域中。Hook 使用了 JavaScript 的闭包机制,而不用在 JavaScript 已经提供了解决方案的情况下,还引入特定的 React API。

useEffect 会在每次渲染后都执行吗? 是的,默认情况下,它在第一次渲染之后和每次更新之后都会执行。(我们稍后会谈到如何控制它。)你可能会更容易接受 effect 发生在“渲染之后”这种概念,不用再去考虑“挂载”还是“更新”。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

参考React中文文档

也就是说useEffect在组件渲染后执行,只要是组件渲染不管是state变化还是props变化或者是首次渲染,useEffect都会执行,把它放在组件内,是因为它在函数组件作用域内干很多事很方便。

在使用过程中发现一些有意思的地方:

1、useState和useEffect都是异步的,当useState改变了,并渲染在页面上了,useEffect才会执行,这个与上面的对应了。

2、useEffect第二个参数是一个数组,可以理解为要监听的对象,props、state都是可以的,其他的数据不行,只有被监听的对象发生变化时,这个useEffect才会被执行。

3、当我们在同一个函数组件中使用多个useEffect会发生什么呢

//  3.1 当使用多个useEffect并且第二个参数不传,如下:
// 如果渲染这个组件,两个useEffect都会执行。
function Hp (props) {
    const [count, setCount] = useState(props.index);
    useEffect(()=>{
        console.log('我是1')
    })
      useEffect(()=>{
        console.log('我是2')
    })
    let someIndex = 0
    return <div ref={val}>
        <Button 
        type='link'
        onClick={()=>{
        someIndex++
            setIndex(index+1)
        }}
        >修改我是index</Button> 
        <p> 我是hooks index {index}</p>
        </div>
}

//  3.3 哪些useEffect会执行,如下:
// 当传入第二个参数,这个参数发生变化时,相应的useEffect就会执行,如果第二个参数不穿,只要发生render就会执行。


import React,{ useState,useEffect } from 'react'
import { Button } from 'antd'

function Hp (props) {
    const { propsIndex } = props
    const val = React.useRef(); 
    const [count, setCount] = useState(propsIndex);
    const [index, setIndex] = useState(0);
    useEffect(()=>{
        console.log('indexcount',2,index)
    },[index])
    useEffect(()=>{
        console.log('indexcount222',2)
    })
    return <div ref={val}>
        <Button 
        type='link'
        onClick={()=>{
            setCount(count+1)
        }}
        >修改count</Button>
        <Button 
        type='link'
        onClick={()=>{
            setIndex(index+1)
        }}
        >修改我是index</Button> 
        <p> 我是hooks PropsIndex {propsIndex}</p>
        <p> 我是hooks count {count}</p>
        <p> 我是hooks index {index}</p>
        </div>
}

hooks和生命周期

首次渲染的时候,useEffect第二个参数不论传什么,首次渲染都会执行。 当传第二个参数不传时,当组件渲染时都会执行,相当于componentDidMount、componentDidUpdate 。 当第二个参数为空数组时,第一次渲染会执行,之后都不会执行类componentDidMount。

useEffect的回调函数的return,如果参数不传,或者非空数组,则会在首次渲染之后每次渲染过后执行,并且他是在回调执行之前执行,相当于componentWillUpdate。

如果参数是传的空数组,则这个return则是在组件销毁的时候执行,相当于componentWillUnMount。