我的朋友们,如果我早点学习这 4 个 React 钩子,也许我可以写出更漂亮的代码。
它们极大地提高了我的工作效率以及代码的可扩展性和可读性。你也一定想学习它们吧?
# 1. useMount
在过去,当组件首次渲染时,我需要发送请求或做一些其他逻辑,我经常写这样的代码,可能像这样。
useEffect(() => {
// fetch request...
}, [])
这很简单,不是吗?但它有一个很大的缺点:语义不够明确,即使我们传入一个空数组。
所以我们可以自定义一个名为 useMount
的钩子,只在组件首次渲染时执行回调函数。
源码
const useMount = (callback) => {
React.useEffect(callback, [])
}
例子’
const UseMountDemo = () => {
const [count, setCount] = React.useState(0)
useMount(() => {
console.log("useMount")
})
return (
<div>
count: { count }
<button onClick={() => setCount(count++)}>add</button>
</div>
)
}
当组件重新渲染时,useMount
不会再次执行,太棒了!
React.useEffect(() => {
return () => {
//Execute when component is Unmounted
}
}, [])
# 2. useUnmount
当组件被卸载并且我们想触发一些逻辑时,像清除计时器,我们会写这样的代码:
显然,我们不能直接看出它是在组件被卸载时执行的。
所以我们需要设计一个钩子 useUnmount
来指示一个回调函数在组件被卸载时执行。
源码
const useUnmount = (callback) => {
React.useEffect(() => {
return callback
}, [])
}
是不是真的那么简单呢?让我们看看如何使用它。
例子
const Child = () => {
const [ count, setCount ] = useState(0)
useUnmount(() => {
// We expect to print out the value of num when the component is unmounted.
console.log('useUnmount', count)
})
return (
<div>
count: {count}
<button onClick={() => setCount(count + 1)}>add</button>
</div>
)
}
const UseUnmountedDemo = () => {
const [showChild, setShowChild] = React.useState(true);
// We use "showChild" to control the display and hiding of the Child component
return (
<div>
{ !!showChild && <Child /> }
<button onClick={() => setShowChild(false)}>Destroy child</button>
</div>
)
}
当“Child”组件被销毁时,useUnmount
回调函数确实被执行了,但我们发现 count
的值为初始值 0,而不是 10,是什么原因导致了这样的错误结果呢?
这是由 useEffect
中的闭包机制造成的,因为传递给组件被卸载时的第一个渲染的函数,为了获取实时状态,我们需要使用 useRef
来实现它。
太棒了,我们也可以在组件被销毁时正确获取 count
的值。
const useUnmount = (callback) => {
const callbackRef = React.useRef(callback)
callbackRef.current = callback
React.useEffect(() => {
return () => {
callbackRef.current()
}
}, [])
}
# 3. useUpdateEffect
有时我们只想在依赖项更改后执行逻辑代码。我们可以通过这样写来实现我们的目标吗?
function UseUpdateEffectDemo() {
const [count, setCount] = React.useState(0)
React.useEffect(() => {
console.log('count changed', count)
}, [ count ])
return (
<div>
count: {count}
<button onClick={() => setCount(count + 1)}>change</button>
</div>
)
}
不幸的是,‘count’ 在组件挂载后会立即打印出 ‘0’。如何在 ‘count’ 改变后执行回调函数呢?
const useUpdateEffect = function (effectCallback, deps = []) {
const isFirstMount = React.useRef(false)
React.useEffect(() => {
return () => {
isFirstMount.current = false
}
}, [])
React.useEffect(() => {
// Do not execute effectcallback for the first time
if (!isFirstMount.current) {
isFirstMount.current = true
} else {
return effectCallback()
}
}, deps)
}
function UseUpdateEffectDemo() {
const [count, setCount] = React.useState(0)
useUpdateEffect(() => {
console.log('Count changed', count)
}, [ count ])
return (
<div>
count: {count}
<button onClick={() => setCount(count + 1)}>change</button>
</div>
)
}
太棒了,这是一个超级实用的钩子。
# 4. useSetState
当我们编写一个 class
组件时,我们使用 this.setState
来操作组件的数据,而当操作对象类型的数据时,setState
更加方便。
const [ person, setPerson ] = React.useState({
name: 'fatfish',
age: 100
})
// Modify name
setPerson({
...person,
name: 'medium'
})
// Modify age
setPerson({
...person,
age: 1000
})
// Use setState to modify name
setState({
name: 'medium'
})
// Use setState to modify age
setState({
age: 1000
})
我们可以实现一个 useSetState
钩子来简化 setPerson
的操作吗?这非常简单,只需对 useState
进行简单的封装。
const useSetState = (initState) => {
const [ state, setState ] = React.useState(initState)
const setMergeState = (value) => {
setState((prevValue) => {
const newValue = typeof value === 'function' ? value(prevValue) : value
return newValue ? { ...prevValue, ...newValue } : prevValue
})
}
return [ state, setMergeState ]
}
例子
function useSetStateDemo() {
const [ person, setPerson ] = useSetState({
name: 'fatfish',
age: 100
})
// Change the value of person in the normal setting mode
const onSetName = () => {
setPerson({ name: 'medium' })
}
//Use the callback function to change the value of person
const onSetAge = () => {
setPerson(() => {
return {
age: 1000
}
})
}
return (
<div>
<p>name: { person.name }</p>
<p>age: { person.age }</p>
<button onClick={onSetName}>change name</button>
<button onClick={onSetAge}>change age</button>
</div>
)
}
# 最后
感谢您的阅读。 我期待您的关注和阅读更多高质量的文章。