如何创建一个可重复使用的React表单组件

450 阅读11分钟

如何创建一个可重复使用的React表单组件

前提条件

在本教程中,人们应该具备以下条件。

  • 基本的React和Javascript知识。
  • 了解npm以及如何从npm安装
  • 在电脑上安装AtomVisual 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;

我们创建一个常量,使用三元操作符检查是否应该在我们的输入组件中呈现一个inputtextarea 元素。我们使用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文件。

Input

第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文件的屏幕截图。

useForm

第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中触发调度,让我们称它为onInputonInput 将触发该输入的调度。

 <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 文件的屏幕截图。

Contact

由于我们正在向输入端传递一个未知的属性,我们需要在我们的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中的情况一样,我们将使用useFormuseState

  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文件的截图。

Auth Auth

第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;

App

结论

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