在React与Typescript中,你可能会被诱惑为组件中的每个字段卷起单独的变化处理程序。今天我将告诉你如何避免这种多余的工作,只写一个变化处理程序
一个简单的用例
这里有一个简单的用例:我们有一个React的表单,有两个文本输入和一个复选框输入。这些输入将填充一个User 对象,它将有以下类型。
type User = {
name: string;
age: number;
admin: boolean;
};
让我们看看这在React组件的上下文中会是什么样子。我们将使用useState 钩子来维护用户对象的内部状态。
import React, { useState } from 'react';
type User = {
name: string;
age: number | null;
admin: boolean;
};
const defaultUser: User = {
name: '',
age: null,
admin: false,
};
function App() {
const [user, setUser] = useState(defaultUser);
return (
<div>
<input value={user.name} />
<input value={user.age || ''} />
<input type="checkbox" checked={user.admin} />
</div>
);
}
处理变化
但如何处理每个属性的变化呢?好吧,我们可以为每个输入创建一个不同的变化处理程序,但这将是多余的。让我们创建一个单一的onUserChange ,为每个输入设置正确的道具。
import React, { useState } from 'react';
type User = {
name: string;
age: number | null;
admin: boolean;
};
const defaultUser: User = {
name: '',
age: null,
admin: false,
};
function App() {
const [user, setUser] = useState(defaultUser);
const onUserChange = <P extends keyof User>(prop: P, value: User[P]) => {
setUser({ ...user, [prop]: value });
};
return (
<div>
<input
value={user.name}
onChange={(e) => {
onUserChange('name', e.target.value);
}}
/>
<input
value={user.age || ''}
onChange={(e) => {
onUserChange('age', parseInt(e.target.value));
}}
/>
<input
type="checkbox"
checked={user.admin}
onChange={() => {
onUserChange('admin', !user.admin);
}}
/>
</div>
);
}
这里的秘诀是我们在onUserChange 处理程序中使用的泛型。通过说prop 的类型是P ,其中P extends keyof User ,我们可以根据第一个参数对onUserChange 的第二个参数进行类型检查。然后,当我们setUser ,typescript编译器就会对我们的类型表示满意。