组件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。有什么事先让浏览器渲染出来再说,不然影响用户体验。