这个系列文章主要是用于练习英文文档的读写能力,期待半年后能脱离工具写出通畅的英文文档。原文地址
我的朋友们,如果我早点学习这 4 个 React hooks,我能写出更优雅的代码。
它们极大的提高了我的工作效率、代码的可伸缩性和可读性。你也想要学习它们吗?
1. useMount
当我们需要在组件第一次渲染发起 HTTP 请求或者一些其他初始化的逻辑时,我们往往会写出如下代码:
useEffect(() => {
// festch request...
}}
这非常简单,但是它有一个很严重的缺点:当我们给 useEffect
传入一个空数组时,语义不够清晰。
所以我们可以自定义一个 hook : useMount
,仅当组件第一次渲染时才会执行对应的回调函数。
const useMount = (callback) => {
useEffect(callback, [])
}
示例
import { useState } from 'react';
const Mount = () => {
const [count, setCount] = useState(0);
useMount(() => {
console.log('first mounted');
});
return <div onClick={() => setCount(count + 1)}>{count}</div>;
};
export default Mount;
2. useUnmount
当我们需要在组件卸载时触发一个逻辑,比如:清理定时器。我们往往会写出如下代码:
useEffect(() => {
return () => {
// 当组件卸载时执行
}
}, []}
显然我们也不能很直观的看出这会在组件卸载时执行。
所以我们需要自定义个 hook:useUnmount
,仅当组件卸载时才会执行回调函数
const useUnmount = (callback) => {
useEffect(() => {
return callback
}, [])
}
示例
import { useState } from 'react';
const Child = () => {
const [ count, setCount ] = useState(0)
useUnmount(() => {
console.log('Child component is unmount', count);
});
return (
<div>
count: {count}
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
};
const Unmount = () => {
const [showChild, setShowChild] = useState(true);
return (
<div>
{showChild && <Child />}
<div onClick={() => setShowChild(!showChild)}>Toggle Child</div>
</div>
);
};
export default Unmount;
当 “Child” 组件卸载时,useUnmuount
的回调函数才真正执行,但是 count
的值会一直是 0,这是为什么呢?
这是因为在 useEffect
内部有一个闭包,回调函数是在组件第一次渲染时传入的。为了获取到最新的状态,我们需要配合 useRef
来实现:
import { useRef, useEffect } from 'react'
const useUnmount = (callback) => {
const callbackRef = useRef(callback)
callbackRef.current = callback
useEffect(() = {
return callbackRef.current()
}, [])
}
3. useUpdateEffect
有时候我们想只在依赖项发生改变时才执行代码逻辑,我们往往会写出如下代码:
import { useState, useEffect } from 'react'
const UpdateEffect = () => {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('count is changed:', count)
}, [count])
return <div onClick={() => setCount(count + 1)}>{count}</div>
}
不幸的是,当组件挂载时会执行一次 useEffect
打印 count
的值为 0。怎么才能实现只有当 count
的值发生变化时才会执行回调函数?
const useUpdateEffect = (callback, deps) => {
const mountedRef = useRef(false);
const callbackRef = useRef(callback);
callbackRef.current = callback;
useEffect(() => {
if (mountedRef.current) {
callbackRef.current();
} else {
mountedRef.current = true;
}
}, [deps]);
useEffect(() => {
return () => mountedRef.current = false
}, []);
};
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
})
我们需要实现一个 hook:useSetState
,来简化 setPerson
的操作。这非常简单,只需要对 useState
进行简单的包装:
import { useState } from 'react'
const useSetState = (initState) => {
const [state, setState] = useState(initState);
const setMergeState = (modifyValue) => {
setState((preState) => {
const newState =
typeof modifyValue === 'function' ? modifyValue(preState) : modifyValue;
return newState
? {
...preState,
...newState,
}
: preState;
});
};
return [state, setMergeState];
};