冷知识:如何优雅的使用原生form表单

720 阅读2分钟

写在开头:由于本人在写文章方面是新人,文章表述能力不足,所以从基础知识开始写起,本文太过基础,大佬不需要观看。

背景

在前端发展越来越快的今天,UI组件库已经非常成熟,项目开发中也离不开这些组件库,它们能帮助我们提高开发效率,统一开发规范。本文不介绍组件库,看看原生form表单有什么值得学习的地方。

React 中原生form

以React为例,我们在使用原生form时会这样写:

import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");
  const [age, setAge] = useState("");
  const [sex, setSex] = useState("");

  const submitForm = async (e) => {
    e.preventDefault();

    const formData = {
      name,
      age,
      sex
    };
   
    console.log('需要传给后端的数据', formData);
  };

  return (
    <div className="App">
      <form onSubmit={submitForm}>
        <div>
          <input

            name="name"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="name"
          />
          <div>{"sds"}</div>
        </div>

        <div>
          <input
            name="age"
            value={age}
            onChange={(e) => setAge(e.target.value)}
            placeholder="age"
          />
        </div>
        <div>
          <input
            name="sex"
            value={sex}
            onChange={(e) => setSex(e.target.value)}
            placeholder="sex"
          />
        </div>
        <br />

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

大家都知道这是受控组件的写法,我们一定需要受控组件吗?如果我只想单纯的提交表单呢。我们可以这样写:

import {useRef} from 'react'
export default function App() {
  const nameRef = useRef('')
  const ageRef = useRef('')
  const sexRef = useRef('')

  const submitForm = async (e) => {
    e.preventDefault();

    const formData = {
      name: nameRef.current.value,
      age: ageRef.current.value,
      sex: sexRef.current.value
    };
   
    console.log('需要传给后端的数据', formData);
  };

  return (
    <div className="App">
      <form onSubmit={submitForm}>
        <div>
          <input
            ref={nameRef}
            name="name"
            placeholder="name"
          />
          <div>{"sds"}</div>
        </div>

        <div>
          <input
            ref={ageRef}
            name="age"
            placeholder="age"
          />
        </div>
        <div>
          <input
            ref={sexRef}
            name="sex"
            placeholder="sex"
          />
        </div>
        <br />

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

但是这样写声明了大量的ref,并不优雅。有没有其他办法获取到所有的表单值呢?

必须有!就是FormData对象。

FormData对象

语法:

let formData = new FormData(form);

参数:

一个 HTML 上的

表单元素——当指定了,这种方式创建的FormData对象会自动将 form 中的表单值也包含进去,包括文件内容也会被编码之后包含进去。

所以我们可以formData对象获取所有的表单项的值,代码如下:

import { useState } from "react";

export default function App() {
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  const submitForm = async (e) => {
    e.preventDefault();

    const formData = new FormData(e.target);

    const dataObject = Object.fromEntries(formData);

    console.log('需要传给后端的数据', dataObject); // {name: '', age: '', sex: ''}
  };

  return (
    <div className="App">
      <form onSubmit={submitForm}>
        <div>
          <input
            name="name"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="name"
          />
          <div>{"sds"}</div>
        </div>

        <div>
          <input
            name="age"
            value={age}
            onChange={(e) => setAge(e.target.value)}
            placeholder="age"
          />
        </div>

        <br />

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

我们在受控和非受控组件中都可以轻松获取所有的表单项的值 ,当表单项过多时也不必一个一个去获取,非常方便。

有人说如果我想校验表单怎么办?

HTML的pattern属性 配合 css伪类:invalid 进行校验

还引用上述例子:

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [name, setName] = useState("");
  const [age, setAge] = useState("");

  const submitForm = async (e) => {
    e.preventDefault();

    const formData = new FormData(e.target);

    const inputObject = Object.fromEntries(formData);

    console.log(inputObject);
  };

  return (
    <div className="App">
      <form onSubmit={submitForm}>
        <div>
          <input
            name="name"
            pattern="[a-z]{3}"  // 正则匹配校验
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="name"
          />
          <div>{"sds"}</div>
        </div>

        <div>
          <input
            name="age"
            pattern="[0-9]{3}"  // 正则匹配校验
            value={age}
            onChange={(e) => setAge(e.target.value)}
            placeholder="age"
          />
        </div>

        <br />

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

css样式:

// styles.css

input:focus {
  outline: none;
}

input:invalid {
  border: red solid 3px;
}

效果如下:

参考

FormData() - Web API 接口参考 | MDN

HTML attribute: pattern - HTML(超文本标记语言) | MDN