《React-函数组件》

276 阅读3分钟

组件App是一个函数,props作为形参传进去。函数组件是可以代替class组件的。函数组件没有state,可以使用React.useState()代替。

const App=(props)=>{
    const [n,setN]=React.useState(0)
    const addN=()=>{
        setN(n+1)
    }
    return (
        <div>
            {n}
            <button onClick={addN}>n+1</button>
        </div>
    )
}

函数组件里没有生命周期,可以使用React.useEffect()代替。这两个API是React v16.8.0推出的Hooks API。

一. useEffect

1. 模拟 componentDidMount

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

useEffect接受一个函数作为参数,第二个参数表示什么时候调用它。第二个参数是一个空数组,就表示只在第一次渲染的时候执行。结果是之后点击按钮更新UI都不会打印。模拟componentDidMount

2. 模拟 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在第一次渲染时也会触发。我们在类组件里第一次渲染是不会触发的,只有数据变化了才会执行。

3. 模拟 componentWillUnmount

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

把组件死之前要做的事情放在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-useUpdate

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

可以自定义一个hook函数

const useUpdate=(fn,dep)=>{
    const [count,setCount]=React.useState(0)
    React.useEffect(()=>{
        setCount(c=>c+1)
    },[dep])

    React.useEffect(()=>{
        if(count>1){fn()}
    },[count,fn])
}

const App=(props)=>{
    const [n,setN]=React.useState(0)
    const onClick=()=>{
        setN(n+1)
    }
    useUpdate(()=>{
        console.log('n变了');
    },n)
    return (
        <div>
            {n}
            <button onClick={onClick}>+1</button>
        </div>
    )
}

自定义一个useUpdate 函数,它同useEffect一样,接受两个参数,第一个是一个函数,第二个是依赖,表示谁变化时触发这个函数。在useUpdate里边,通过一个中间变量count ,当传进去的依赖dep变化时,把count+1,当count>1时,才执行函数。由于count的初始值是0,第一次渲染时count变成1,此时没有执行fn。就躲过了第一次渲染。

三. useLayoutEffect

useEffect会在浏览器对真实的DOM节点渲染完成后,执行。

useLayoutEffect会在把虚拟DOM=>真实DOM后,浏览器绘制之前执行。

这两个是有时间差的,useLayoutEffect总是会比useEffect先执行

不过我们都优先使用useEffect。有什么事先让浏览器渲染出来再说,不然影响用户体验。