1. 什么是自定义Hook
自定义Hook是React16.8版本引入的新特性,它能够让我们重用组件之间共享的逻辑代码。实际上,自定义Hook就是一个JavaScript函数,它们跟普通函数在语法和实现上没有区别,但是它们的名称必须以"use"开头,这是React的规定。自定义Hook是一种设计模式,它通过定义可复用的逻辑代码,使我们的组件更加简洁、易于维护。
自定义Hook有一些重要的特点:
自定义Hook只是一种用于重用代码的技巧,不是开发React组件的必要条件。
自定义Hook不能用于普通的JavaScript函数中,只能用于React函数组件和自定义Hook。
自定义Hook仍然遵循React的基本原则,包括state、生命周期方法、context等。
自定义Hook不能用于class组件中,只能用于函数式组件。
2. 自定义Hook的优点
2.1 提高代码的复用性
自定义Hook可以将组件之间共享的业务逻辑代码提取出来,形成独立的模块,进而实现组件的复用和逻辑的复用。这样就可以让组件变得更加简洁、易于维护。例如,实现一个异步请求的自定义Hook,就可以被多个组件复用。
2.2 更好的逻辑分离
使用自定义Hook可以将组件之间的业务逻辑和组件自身的状态逻辑分离开来。这样,组件只需要关注自身的状态和UI渲染,而不需要关注其他组件之间的业务逻辑。这样就实现了逻辑的解耦,提高了代码的可维护性。
3. 如何创建自定义Hook
创建自定义Hook很简单,只需要创建一个普通的JavaScript函数,并以"use"开头。在函数内部,我们可以定义状态、计算和更新逻辑,一旦确定了我们要抽象出的逻辑,就可以将代码封装为一个自定义Hook。
一个最简单的自定义Hook的代码如下:
function useExample() {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return { count, increment };
}
function Example() {
const { count, increment } = useExample();
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
在上面的例子中,我们定义了一个名为"useExample"的自定义Hook,它根据逻辑返回由count和increment两个数值组成的对象。然后,在组件中调用"useExample"自定义Hook,就可以得到count和increment两个数值,这相当于我们在多个组件之间共享了这个Hook的逻辑代码。
4. 实战:使用自定义Hook处理表单提交
4.1 需求与实现
我们假设有一个表单组件,包括三个输入框(姓名、电子邮件、手机号码),以及一个提交按钮。我们需要编写一个自定义Hook,用于处理表单的提交事件,当用户点击提交按钮时,将表单数据作为参数传入到自定义Hook中,并且进行相应的表单验证和提交操作。
首先,我们定义一个自定义Hook,名为"useForm":
function useForm(callback, validate) {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (validate) setErrors(validate(values));
callback();
};
const handleChange = (event) => {
event.persist();
setValues(values => ({ ...values, [event.target.name]: event.target.value }));
};
return {
handleChange,
handleSubmit,
values,
errors,
};
}
在上面的代码中,我们定义了一个包含三个状态的Hook:values(用于存储表单数据)、errors(用于存储表单验证状态)、callback(用于提交表单的回调函数)。
handleChange函数用于处理表单输入框的变化事件,在变更的时候设置相应值。它使用了event.persist()方法保留合成事件,否则的话,在异步空间使用event时会引发错误。
handleSubmit函数用于处理表单提交事件,在提交前执行表单验证逻辑,如果有错误,则设置相应的错误信息;如果表单数据正确,则使用callback函数进行提交。
最后,将上述的Hook应用到表单组件中:
function App() {
const { handleChange, handleSubmit, values, errors } = useForm(submit, validate);
function submit() {
console.log(values);
}
function validate(values) {
let errors = {};
if (!values.name) {
errors.name = "姓名不能为空";
}
if (!values.email) {
errors.email = "邮箱不能为空";
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
errors.email = "邮箱格式不正确";
}
if (!values.mobile) {
errors.mobile = "手机号码不能为空";
} else if (!/^[1]\d{10}$/.test(values.mobile)) {
errors.mobile = "手机号码格式不正确";
}
return errors;
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<label>姓名:</label>
<input type="text" name="name" onChange={handleChange} value={values.name || ""} />
{errors.name && (
<strong>{errors.name}</strong>
)}
</div>
<div>
<label>邮箱:</label>
<input type="email" name="email" onChange={handleChange} value={values.email || ""} />
{errors.email && (
<strong>{errors.email}</strong>
)}
</div>
<div>
<label>手机号码:</label>
<input type="tel" name="mobile" onChange={handleChange} value={values.mobile || ""} />
{errors.mobile && (
<strong>{errors.mobile}</strong>
)}
</div>
<button type="submit">提交</button>
</form>
</div>
);
}
在上面的代码中,我们将App组件中重复的表单逻辑封装到了"useForm"自定义Hook中,使得App组件变得更加简洁、易于阅读和维护。
4.2 Demo
下面是一个在线的示例,可以看到自定义Hook的代码已经被重用多次了:
https://codesandbox.io/s/form-validation-with-custom-hook-i88l9