React:如何创建一个自定义hook
React引入Hooks已经有一段时间了。随着它们的发布,Hooks通过内置的hook为函数组件提供了状态管理和副作用的能力,例如useState和useEffect。
然而React提供的内置hook很少(例如:useReducer,useCallBack,useMemo,useContext)。不过,以这些内置hook作为基础,React开发者可以创建自己的自定义hook。在这个教程作为学习实践,我将带你创建一个自定义hook。
在我们创建一个自定义hook,你应该知道创建一个hook的两个规则:
- 自定义hooks命名需要以"use"作为前缀。例如,一个自定义hook可以被命名useLocalStorage或useAuthentication。在我们的例子中,这个自定义的hook会被命名为useBoolean。
- 自定义hooks由React内置的hook或其他的自定义hook组成。因此一个自定义hook本质是一个或多个hook构成的新组合。如果一个自定义hook没有使用任何的内部hook,那它就不是一个自定义hook,也不应该以"use"为前缀。
我们会创建一个自定义hook命名为useBoolean,当我作为一个React自由职业者参与一个新项目时,我几乎都会使用它。不过在我们实现这个hook之前,看下它为我们解决了什么问题。让我们以一个小例子开始:
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;
这个组件渲染了一个可以切换一个布尔值的按钮。在真实世界的React应用中,一个状态布尔值对你来说可做的并不多。要么你可以切换它(就像前面的例子),要么直接将它设置为 true 或 false (就像下面的例子) :
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>
);
}
export default App;
一些开发者可能会争辩,我们可以使用内联处理来代替,这样就不会有重复声明的事件处理。但是,就我个人而言会尽可能避免内联处理,因为它们会在jsx中注入太多逻辑,而应该在组件的函数签名和返回语句之间定义函数来替代。不过这只是个人偏好。
总之,你每次使用状态布尔值,你都会遇到相同的实现细节:切换布尔值或者将其设置为两个可能值中的一个。在多个React组件中使用状态布尔值时,为了规避这段重复代码,我开始创建一个自定义hook。
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的hook中。另外,这个自定义hook返回一个数组,包含状态和一些更新状态函数。
从一个自定义hook中需要返回多个值时最佳的做法是返回一个数组,因为React内置的hooks在返回多个值时,都会利用数组和数组解构。使用数组解构带来的好处就是可以指定任何变量名(相比于在对象解构中的重命名代码量更少)。
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,
},
];
};
一个很好的补充是增加了一个初始状态(如上一个代码片段所示)。回到我们的应用组件中,我们可以通过传递一个初始状态使用这个新的自定义hook,利用它的返回值来显示和更新状态。
function App() {
const [isToggle, { setToggle }] = useBoolean(false);
return (
<div>
<button type="button" onClick={setToggle}>
Toggle
</button>
{isToggle.toString()}
</div>
);
}
由于这个自定义hook不仅提供切换状态布尔值的更新函数,还可以显式设置true或者false,所以我们都可以使用这些函数:
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>
);
}
本质上我们在一个自定义hook中封装了一个状态布尔值和所有相关的事件处理。当我们需要一个状态布尔值时使用这个自定义hook,可以不用再定义实现控制这个状态布尔值相关的事件处理而是使用hook返回值中的函数。
作为总结,我们学习了如何使用React内置hook--useState创建一个自定义hook,这个自定义hook虽然并不复杂,但它向你展示了如何在自己的React项目中降低复杂度或冗余度。
这有很多自定义的React hook用于解决各种各样的问题。他们大多数可以在通过npm安装。但是,当我发现对于对我来说很好的hook时,我都会尝试简单的实现它。以下可能是你可能想要查看的内容: