React函数组件详解

5,482 阅读3分钟

一、创建一个函数组件

const Hello = (props) => {
    return <div>{props.message}</div>
}
// 或者这样写
const Hello = props => <div>{props.message}</div>
// 常用自写法
function Hello(props){
    return <div>{props.message}</div>
}

函数组件代替类组件

函数组件可以代替类组件,因为函数组件语法更加简单易懂,但是也面临以下两个问题

  • 没有state
  • 没有生命周期

二、没有state的解决方法

React v16.8.0推出Hooks API,其中的 useState 可以解决这个问题

注意useState 不可以用 if...else

import React, { useState, useEffect } from "react"

const App = props =>{//消除了this
    const [n, setN] = React.useState(0)//数组前面是读,后面是写,叫法无所谓
    const onClick = () => {
        setN(n+1)
    }
    return {
        <div>
        {n},
        <button onClick = {onClick}>+1</button>
        </div>
    }
}

三、没有生命周期的解决方法

React v16.8.0推出Hooks API,其中的 useEffect 可以解决这个问题

默认每次渲染都会调用

import React, { useState, useEffect } from "react"

useEffect(()=>{ console.log('数据更新了') },)

useEffect 接受一个函数作为参数,第二个参数表示什么时候调用它

模拟 componentDidMount

import React, { useState, useEffect } from "react"

useEffect(()=>{ console.log('第一次渲染') },[])

第二个参数的空数组表示只在第一次调用,结果是之后点击按钮更新UI都不会打印,以此模拟componentDidMount

模拟 componentDidUpdate

React.useEffect(()=>{
    console.log('n变了')
},[n])

想让哪个数据更新之后,执行代码,就把那个数据放到数组里。只有n变了,才会触发。如果m变了,就不会执行。

React.useEffect(()=>{
    console.log('n,m变了')
},[n,m])

数组里也可以接受多个数据

React.useEffect(()=>{
    console.log('state变了')
})

如果state里有多个数据,想做到不管哪个数据变化,都执行这个函数,就不写第二个参数。比如有n和m,此时不管是n+1,还是m+1,都会打印

但是 useEffect 在第一次渲染时也会触发。我们在类组件里第一次渲染是不会触发的,只有数据变化了才会执行。

模拟 componentWillUnmount

import React, { useState, useEffect } from "react"

useEffect(()=>{
  console.log('第一次渲染')
  return ()=>{
  console.log('组件要死了')
   }
})

return的函数会在销毁时执行,因此需要把组件死之前要做的事情放在return后边

const App=(props)=>{
    const [childVisible,setChildVisible]=React.useState(true)
    //数组前面是读,后面是写,叫法无所谓
    const hide=()=>{
        setChildVisible(false)
    }
    const show=()=>{
        setChildVisible(true)
    }
    return (
        <div>
            {childVisible? <Child /> : null}
            {childVisible? <button onClick={hide}>hide</button> : <button onClick={show}>show</button>}
        </div>
    )
}
const Child=()=>{
    React.useEffect(()=>{
        return ()=>{
            console.log('child死了');
        }
    })
    return <div>Child</div>
}

当点击hide按钮时,Child消失了,有打印。 如果同时存在多个 useEffect,会按照出现的顺序执行。

自定义Hook

React.useEffect() 当使用它模拟更新后执行函数时,比如n更新后就做什么事情,它会把第一次渲染也算上。有没有什么方法能让它不算第一次渲染的,只在n真正变化时才执行。

可以自定义一个hook函数,这个函数必须是use开头的

import React, { useState, useEffect } from "react"

const useUpdate  = (fn, dep) =>{
    const [count, setCount] = useState(0)
    useEffect(()=>{
      setCount(x=> x + 1)  
    }, [dep]);//这里的dep就相当于n
    
    useEffect(()=>{
    if(count > 1){
        fn()
      }
    }, [count, fn]); 
}

  useUpdate(()=>{
    console.log('变了')
  },n)

四. useLayoutEffect

  • useEffect 会在浏览器对真实的DOM节点渲染完成后,执行。
  • useLayoutEffect会在把虚拟DOM=>真实DOM后,浏览器绘制之前执行。
  • 这两个是有时间差的,useLayoutEffect 总是会比 useEffect 先执行
  • 不过我们都优先使用 useEffect。有什么事先让浏览器渲染出来再说,不然影响用户体验。