如何创建一个可重复使用的React表单组件
前提条件
在本教程中,人们应该具备以下条件。
- 基本的React和Javascript知识。
- 了解npm以及如何从npm安装
- 在电脑上安装Atom或Visual studio代码和npm。
目录
- React表单
- 设置我们的应用程序
- 创建可重复使用的输入组件
- 创建一个React钩子组件
- 创建联系表单和注册/登录组件
- 添加路由以显示我们的内容
目标
- 创建一个可重复使用的表单,可以在任何组件中呈现。
- 创建一个自定义的React钩子函数。
让我们开始吧。
React表单和输入
一个表单包含数据。React表单是从前端(浏览器)得到的数据,由React代码中的组件处理。
Input 是一个HTML元素,用于创建接受和获得用户数据的互动控件。
在React中,我们使用JSX,它代表Javascript XML。这使得我们可以在React中编写HTML。
第1步 - 设置我们的应用程序
首先,我们需要在我们的代码编辑器中打开一个文件夹,无论是atom还是visual studio code。我们在代码编辑器中打开终端,并输入以下命令来安装React和它的一些依赖项。
npx create-react-app my-form
按回车键进行安装。
在终端中输入cd my-form ,进入my-form 文件夹。接下来,我们输入npm start ,启动我们的开发服务器。我们的React应用程序可以在http://localhost:3000 上看到。
我们需要改变安装时默认编写的预定义的React代码。
在你的浏览器中进入http://localhost:3000 ,一个显示React标志的网站会出现。
让我们来改变React代码。
首先,我们在visual studio代码编辑器中打开app.js 文件。接下来,我们删除div 标签内的所有代码,添加下面的代码。
import React from 'react'; //for React and EJX
import './App.css' // for styles
const App = () => {
return (
<div className={/*style name*/}>
{/*Our codes will be added here */}
</div>
);
}
export default App;
第2步 - 创建可重复使用的输入组件
可重复使用的组件是一块用户界面,可以在一个应用程序的许多部分中使用,以建立和渲染不同的用户界面实例。
我们首先在src 文件夹中创建一个component 文件夹,在这个组件文件夹中,我们创建一个名为Input.js 的文件。
在Input.js文件内,在文件的顶部,我们首先从react导入React。然后,我们创建一个名为Input 的函数,将props作为一个参数。
它应该看起来像这样。
import React from 'react';
const Input = (props) => {
return(
);
}
export default Input;
我们创建一个常量,使用三元操作符检查是否应该在我们的输入组件中呈现一个input 或textarea 元素。我们使用props来获取当时正在DOM上渲染的组件的值。
const element = props.element === "input" ? (
<input name={props.name} type={props.type}
placeholder={props.placeholder} value=""
/>
) : (
<textarea name={props.name} rows= "4"value=""
/>
);
然后我们返回一个div ,其中包含一个标签元素和我们的元素常量。
const Input = (props) => {
return(
<div>
<label htmlFor={props.id}> {props.label}</label>
{element}
</div>
)
}
export default Input;
之后,我们现在可以添加我们的状态逻辑。在本教程中,我们将使用useReducer 来处理状态逻辑。我们也可以使用useState ,但如果你想处理多个状态,useReducer 是最好的。
useReducer 是一个接受两个参数的React钩子,一个reducer和一个initialState。reducer包含了你的状态逻辑,类似于 ,而initialState是你的初始状态。 也会返回你的当前状态和一个调度方法,用来触发状态逻辑。setState useReducer
我们首先从React导入,然后定义一个常量来保持我们在Input函数之外的reducer。
const inputReducer = (state, action) => {
switch (action.type) {
case 'CHANGE':
return { ...state, value: action.val };
default:
return state;
}
}
我们使用一个switch 语句来检查在我们的逻辑被触发之前是否满足一个案例。inputReducer 函数接受两个参数,状态和动作。状态是我们当前的状态,而动作则是我们调度函数的值。
在输入函数中,我们定义我们的Reducer状态,并将inputReducer 作为一个参数,我们可以向我们的状态添加其他值或项目。然后使用数组销毁来给我们的useReducer分配两个值。
如下图所示。
const [inputState, dispatch] = useReducer(inputReducer, {value: ''});
然后我们定义一个函数,它将持有触发状态变化的调度。
const changeHandler = event => {dispatch({type: 'CHANGE', val: event.target.value}); }
然后我们给我们的输入添加一个新的属性,叫做onChange ,并把changeHandler 。
Input.js文件。

第3步 - 创建一个React钩子组件
我们需要在我们的项目中创建一个自定义的React钩子,以避免代码重复。创建一个自定义的钩子,就是在一个函数中使用一个React钩子,我们可以多次使用,而不是让重复的代码执行相同的功能。这将保存我们的useReducer 和返回值,我们可以在我们项目的任何文件中使用,因为我们将在我们的组件中使用useReducer 。
因此,在我们的组件文件夹中,我们创建了一个名为hooks的新文件夹,在这个文件夹中,我们创建了一个名为form-hook.js 的JavaScript文件。
就像在我们的输入文件中一样,useReducer需要有一个常量来定义我们的状态应该如何改变的逻辑。
import {useReducer} from 'react';
const formReducer = (state, action) => {
switch(action.type) { case 'INPUT_CHANGE':
return { ...state, inputs: { ...state.inputs, [action.inputId]: {value: action.value} }
};
default:
return state
}
}
然后,我们创建一个函数并将其命名为useForm 。
注意:当创建一个自定义钩子时,我们必须使用小写字母,然后是大写字母。
在这个函数中,我们定义了我们的Reducer函数和一个函数来分配我们的动作
export const useForm = (initialInput) => {
const [formState, dispatch] = useReducer(formReducer, {inputs: initialInput });
const inputHandler = (id, value) => { dispatch(
{type: 'INPUT_CHANGE', value: value, inputId: id})
}
return [formState, inputHandler];
}
我们接收必要的值作为道具,我们称之为initialInput。它将作为我们的逻辑的初始状态。然后我们返回我们的formState ,它持有初始状态和inputHandler,它持有我们的调度函数。
我们的form-hook.js文件的屏幕截图。

第4步 - 创建联系表格和注册/登录组件
我们首先在组件文件夹中创建一个Contact.js 文件和一个Auth.js 文件。在Contact.js文件中,我们首先导入React,因为我们将在这个文件中使用我们的Input组件,我们也需要将Input组件和useForm导入我们的应用程序中。
我们创建一个返回表单元素的函数,在表单元素中,我们渲染Input并将所需的属性作为props传递。我们还需要创建一个处理程序来处理提交动作,让我们把它叫做submitHandler 。在这个函数中,我们可以请求或获得表单输入中发生的事件。我们把submitHandler 传递给我们的表单元素,这样它就会在提交事件发生时被调用。
import React from 'react'; //for React and EJX
import Input from './input'; //our Reusable component
import { useForm } from './hook/form-hook';
const Contact = (props) => {
const submitHandler = (event) => { event.preventDefault();
console.log(formState.inputs);
}
return (
<form onSubmit={submitHandler}>
<Input id="name" element="input" type="text" label="Name"
/>
<Input id="email" element="input" type="e-mail" label="E-mail"
/>
<Input id="description" element="textarea" label="Description"
/>
<Input id="address" element="input" type="text" label="Address"
/>
<Input id="number" element="input" type="number" label="Number"
/>
<button type="submit"> SUBMIT</button>
</form>
);
}
export default Contact;
然后,我们使用数组销毁法提取从useForm返回的函数。在我们的useForm钩子中,我们传递我们的初始状态
const [formState, inputHandler] = useForm({
name: {value: ''},
email: {value: ''},
description: {value: ''},
address: {value: ''},
number: {value: ''}
});
更多的输入可以被添加到我们的状态中,只要输入是在我们的当前状态中。
现在,我们给我们的输入传递一个属性,它将在我们的useForm中触发调度,让我们称它为onInput 。onInput 将触发该输入的调度。
<form onSubmit={submitHandler}>
<Input id="name" element="input" type="text" label="Name"
onInput={inputHandler}
/>
<Input id="email" element="input" type="e-mail" label="E-mail"
onInput={inputHandler}
/>
<Input id="description" element="textarea" label="Description"
onInput={inputHandler}
/>
<Input id="address" element="input" type="text" label="Address"
onInput={inputHandler}
/>
<Input id="number" element="input" type="number" label="Number"
onInput={inputHandler}
/>
<button type="submit"> SUBMIT</button>
</form>
我们的 contact.js 文件的屏幕截图。

由于我们正在向输入端传递一个未知的属性,我们需要在我们的input.js 文件中处理它。然后我们回到我们的input.js文件,导入useEffect ,以处理我们输入端的变化。
useEffect 是一个钩子,接受一个回调函数和依赖关系作为参数。这在一个功能组件中管理副作用。
所以在我们的input.js文件中,在我们的输入函数中。我们还导入了useEffect ,并将我们的onInput 作为回调函数。这样,在每一个变化中,useEffect 将处理它。通过这样做,我们还需要使用对象重构来提取所需的精确值,并在我们的表单钩子中使用useCallback ,以防止不必要的变化,从而触发无限循环。
import {useEffect} from 'react';
const {id, onInput} = props;
const {value} = inputState;
useEffect(() => {
onInput(id, value)
}, [id, onInput, value])
同样在我们的表单钩子文件中,我们导入useCallback来处理inputHandler,因为那是传递给我们的onInput的东西。
import {useCallback} from 'react';
const inputHandler = useCallback(id, value) => {
dispatch({type: 'INPUT_CHANGE', value: value, inputId: id})
}
接下来,我们在组件文件夹中创建一个名为Auth.js的新文件。在这个文件中,我们将用我们的输入组件创建一个注册和登录页面,并使用状态在注册和登录之间切换,以便其中一个被呈现在屏幕上。这个文件将类似于我们的Contact.js文件,但没有什么区别。
就像我们创建Contact.js时一样,我们首先导入React和{useState}。正如Contact.js中的情况一样,我们将使用useForm 和useState
import {useState} from 'react';
import { useForm } from './hook/form-hook';
const Auth = (props) => {
const [formState, inputHandler] = useForm({
email: { value: ''}, password: {value: ''}
})
const [isLoginMode, setIsLoginMode] = useState(true);
const submitHandler = (event) => { event.preventDefault();
console.log(formState.inputs);
}
我们使用useState 来设置我们的登录模式,以便在浏览器中呈现一个登录或注册页面。
return (
<>
<form onSubmit={submitHandler}>
{!isLoginMode && (
<Input element="input" id="name" type="text" label="Your Name"
onInput={inputHandler}
/>
)}
<Input id="email" element="input" type="email" label="E-mail" onInput={inputHandler}
/>
<Input id="password" element="input" type="password" label="Password"
onInput={inputHandler}
/>
<button type="submit">{isLoginMode ? 'LOGIN' : 'SIGNUP'}</button>
<button type="button" onClick={}
>SWITCH TO {isLoginMode ? 'SIGNUP' : 'LOGIN'} </button>
</form>
</>
);
export default Auth;
在我们的Auth文件中,我们需要处理不同的状态变化。当注册时,我们需要三个输入和状态。当登录时,我们需要两个输入和状态。因此,我们需要在我们的useForm中建立一个新的案例和调度,将在我们的Auth.js中使用。
为了实现这一点,我们将在inputhandler之后添加一个新的处理程序,它将设置我们的表单数据,这个处理程序将从我们的Auth.js文件中接收表单数据。
const setFormData = useCallback((inputData) => {
dispatch({type: 'SET_DATA', inputs: inputData})
}, []);
然后我们也返回setFormData,以便在我们的Auth.js文件中使用它。我们使用数组销毁来提取它,就像我们在联系页面中做的那样
const [formState, inputHandler, setFormData] = useForm({
email: { value: ''},
password: {value: ''}
})
但是,在我们使用它之前,我们要创建一个函数来切换isLogged 状态,并改变被发送到useForm的初始状态。
const switchModeHandler = () => {
if(!isLoginMode) {
setFormData({
name: undefined
})
} else {
setFormData({
...formState.inputs,
name: {value: ''}
})
}
setIsLoginMode(prevMode => !prevMode);
};
然后我们将我们的switchModeHandler 函数传递给我们的按钮onclick事件。这将在按钮被点击时触发该函数,然后该函数检查isLogged状态并切换发送到useForm的输入数据。
我们的auth.js文件的截图。

第5步 - 添加Routes来显示我们的内容
我们需要向我们的应用程序添加Routes。为了看到我们的组件,因为我们不希望在主页上有我们的注册和联系人组件,我们需要为Auth文件创建一个路由,而联系人则在App.js文件中被呈现。
要在我们的App.js文件中添加路由,我们首先需要安装并将其从react-router-dom 中导入我们的app.js 文件。为了安装react-router-dom ,我们在终端写下以下内容。
`npm install react-router-dom`
按回车键来安装它。
接下来,我们再从react-router-dom导入BrowserRouter、Routes和Route。我们将BrowserRouter包裹在开口和闭口标签中,在它里面我们将Routes也包裹在开口和闭口标签中,并将Route包裹在一个自闭口标签中。如果没有用Routes包装,我们就不能在Route中渲染我们的元素。在渲染Route时,我们需要添加两个属性,叫做element和path,element接受我们想要渲染到DOM中的组件,path接受我们希望组件被渲染的位置。
如下图所示。
import {Route, BrowserRouter, Routes} from 'react-router-dom';
const App = () => {
return (
<div className={/*style name*/}>
<BrowserRouter>
<Routes>
<Route/>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
然后我们把我们的Auth.js和Contact文件导入App.js文件,并在返回括号内渲染。在我们的浏览器DOM中,当我们访问localhost:3000 ,我们会看到Contact页面。当我们访问localhost:3000/auth ,我们会看到Auth页面。
我们的App.js文件应该看起来像下面这样。
import {Route, BrowserRouter, Routes} from 'react-router-dom';
const App = () => {
return (
<div className={/*style name*/}>
<BrowserRouter>
<Routes>
<Route path='/auth' element={<Auth/>}/>
<Route path="/" element={<Contact />} />
</Routes>
</BrowserRouter>
</div>
);
}
export default App;

结论
在本教程中,我们创建了一个可重复使用的表单。这是通过将表单的输入逻辑放在一个组件中,并在另一个组件中进行渲染来实现的。在上面的教程中,我们不是每次都写表单逻辑,而是在一个文件中定义逻辑,并在我们需要的地方使用该逻辑。我们通过props获得要显示的信息,并通过React钩子处理值和状态。