React在相当长的时间里引入了Hooks。随着他们的发布,Hooks给了功能组件使用状态和副作用的内置Hooks的能力,如React的useState Hook和React的useEffect Hook。
虽然React只提供了少量的内置Hooks(如useReducer、useCallback、useMemo、useContext)。但是,通过使用这些Hooks作为基础,React开发者可以创建他们自己的Hooks,称为自定义Hooks。在本教程中,我将指导你创建一个自定义钩子作为学习经验。
在我们创建一个自定义钩子之前,你需要知道创建一个钩子有两个规则:
- 自定义钩子是以 "使用 "作为前缀来命名的。例如,一个自定义钩子可以被命名为useLocalStorage或useAuthentication。在我们的例子中,自定义钩子将被命名为useBoolean。
- 自定义钩子由内置的React钩子或其他自定义钩子组成。因此,一个自定义Hook总是一个或多个Hook的新组合。如果一个自定义Hook内部不使用任何Hook,它就不是一个自定义Hook,也不应该有 "use "这个前缀。
我们将创建一个名为useBoolean的自定义Hook,当我作为React自由职业者加入一个新项目时,我几乎每次都会用到它。但在我们实现这个钩子之前,让我们看看它为我们解决了什么问题。让我们从一个小例子开始。
import * as React from 'react';
function App() {
const [isToggle, setToggle] = React.useState(false);
const handleToggle = () => setToggle(!isToggle);
return (
<div>
<button type="button" onClick={handleToggle}>
Toggle
</button>
{isToggle.toString()}
</div>
);
}
export default App;
Togglefalse
该组件渲染了一个按钮,可以切换一个布尔值。在现实世界的React应用中,你对一个有状态的布尔值没有什么可做的。要么你切换它(就像前面的例子),要么你明确地把它设置为真或假(就像下面的例子)。
import * as React from 'react';
function App() {
const [isToggle, setToggle] = React.useState(false);
const handleToggle = () => setToggle(!isToggle);
const handleTrue = () => setToggle(true);
const handleFalse = () => setToggle(false);
return (
<div>
<button type="button" onClick={handleToggle}>
Toggle
</button>
<button type="button" onClick={handleTrue}>
To True
</button>
<button type="button" onClick={handleFalse}>
To False
</button>
{isToggle.toString()}
</div>
);
}
ToggleTo TrueTo Falsefalse
有些开发者可能会说,我们可以用内联处理程序来代替,这样就不会出现事件处理程序的重复声明。然而,就我个人而言,我尽量避免内联处理程序,因为它们在JSX中注入了太多的逻辑,而这些逻辑应该在组件的函数签名和返回语句之间定义。但这只是个人偏好。
总之,每次你使用有状态的布尔值时,你都会遇到相同的实现细节。要么你切换布尔值,要么把它设置为两个可能的值之一。当在多个React组件中使用有状态布尔值时,为了屏蔽这种重复的代码,我开始为它创建一个自定义钩子。
const useBoolean = () => {
const [state, setState] = React.useState();
const handleTrue = () => setState(true);
const handleFalse = () => setState(false);
const handleToggle = () => setState(!state);
return [
state,
{
setTrue: handleTrue,
setFalse: handleFalse,
setToggle: handleToggle,
},
];
};
基本上所有的实现细节,也就是状态和事件处理程序,都转移到这个叫做useBoolean的自定义钩子中。此外,这个自定义钩子以数组的形式返回状态和更新状态的函数。
当从自定义钩子返回多个值时,返回数组是一种最佳做法,因为React的内置钩子--在返回多个值的情况下--会使用数组,因此也会使用数组析构法。使用数组重构的好处是可以给被重构的值任意命名(比在对象重构的情况下重命名值的代码少)。
const useBoolean = (initialState = false) => {
const [state, setState] = React.useState(initialState);
const handleTrue = () => setState(true);
const handleFalse = () => setState(false);
const handleToggle = () => setState(!state);
return [
state,
{
setTrue: handleTrue,
setFalse: handleFalse,
setToggle: handleToggle,
},
];
};
一个很好的补充就是添加一个初始状态(正如在最后一个代码片断中看到的)。回到我们的应用程序组件中,我们可以利用这个新的自定义钩子,将初始状态传递给它,并使用其返回的值来显示状态和更新它。
function App() {
const [isToggle, { setToggle }] = useBoolean(false);
return (
<div>
<button type="button" onClick={setToggle}>
Toggle
</button>
{isToggle.toString()}
</div>
);
}
由于自定义钩子不仅提供了切换状态布尔值的功能,还提供了将其明确设置为真或假的功能,我们也可以利用这些功能。
function App() {
const [isToggle, {
setToggle,
setTrue,
setFalse,
}] = useBoolean(false);
return (
<div>
<button type="button" onClick={setToggle}>
Toggle
</button>
<button type="button" onClick={setTrue}>
To True
</button>
<button type="button" onClick={setFalse}>
To False
</button>
{isToggle.toString()}
</div>
);
}
ToggleTo TrueTo Falsefalse
从本质上讲,我们把有状态的布尔值和所有的事件处理程序--这些都是对布尔值的操作--提取到一个自定义钩子中。每次我们需要一个有状态的布尔值时,通过使用这个自定义钩子,我们就可以省去定义事件处理程序,其中包括如何操作布尔值的实现细节,而使用从钩子返回的函数。
总之,我们已经学会了如何通过使用React的一个内置钩子来创建一个自定义钩子,这个钩子叫做useState。这个自定义钩子并不复杂,然而,它应该告诉你如何在你的React项目中减少复杂性和冗余性。