在表单中演示可复用的 React 组件

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

组件是 React 应用程序的构建块。几乎不可能构建一个 React 应用程序而不使用组件。它非常普遍,以至于一些第三方软件包为您提供了可用于将功能集成到应用程序中的组件。

这些第三方组件往往是可复用的。它们与您可能在应用程序中使用的组件之间的区别在于特定性

我的意思是,假设您经营一家销售 polo 衫的公司。您可以做两件事

  1. 您可以生产已经印有设计的 polo 衫,或者
  2. 您可以让买家选择他们想要的图案。

一些基本的东西将保持一致,例如所有 polo 衫都应该是短袖的。但用户可以选择衬衫的款式,例如颜色和尺寸。在这种情况下,短袖 polo 衫将成为一个很好的 React 组件:它是同一件物品,但有不同的款式。

现在假设您正在开发一个登录表单。与 polo 衫一样,表单具有一致的特征,但我们关注的不是尺寸和颜色的变化,而是输入字段、提交按钮,甚至可能还有忘记密码链接。这可以被组件化并实现输入、按钮、链接等的变体。

输入元素示例

让我们从创建表单输入字段的角度来看。以下是如何将典型的文本输入作为 React 组件显示:

class Form extends React.Component {
  this.state = {
    username: ''
  }
  
  handleChange = (event) => {
    this.setSate({ username: event.target.value })
  }

  render() {
    return (
      <input
        name="username"
        type={type}
        placeholder="Enter username"
        onChange={this.handleChange}
        value={this.state.username}
      />
    )
  }
}

为了使此输入元素可在其他地方和项目中复用,我们必须将其提取到其自己的组件中。让我们称之为FormInput

const FormInput = ({
  name,
  type,
  placeholder,
  onChange,
  className,
  value,
  error,
  children,
  label,
  ...props
}) => {
  
  return (
    <React.Fragment>
      <label htmlFor={name}>{label}</label>
      <input
        id={name}
        name={name}
        type={type}
        placeholder={placeholder}
        onChange={onChange}
        value={value}
        className={className}
        style={error && {border: 'solid 1px red'}}
      />
      { error && <p>{ error }</p>}
    </React.Fragment>
  )
}

FormInput.defaultProps = {
  type: "text",
  className: ""
}

FormInput.propTypes = {
  name: PropTypes.string.isRequired,
  type: PropTypes.string,
  placeholder: PropTypes.string.isRequired,
  type: PropTypes.oneOf(['text', 'number', 'password']),
  className: PropTypes.string,
  value: PropTypes.any,
  onChange: PropTypes.func.isRequired
}

该组件接受某些 props,例如我们需要使用有效标记创建输入所需的属性,包括占位符、值和名称。我们在 render 函数中设置输入元素,将属性值设置为传递给组件的 props。我们甚至将输入绑定到标签以确保它们始终在一起。您可以看到我们没有通过预定义任何内容来做出假设。目的是确保该组件可以在尽可能多的场景中使用。

这使得它成为一个好的组件,因为它强制执行良好的标记(Brad Frost 称之为愚蠢的 React),这表明并非每个组件都必须是某种高度复杂的功能。再说一次,如果我们谈论的是非常基本的东西,比如静态标题,那么使用 React 组件可能就有点过头了。创建可复用组件的可能标准可能是当您需要在应用程序的其他部分使用相同的功能时。如果该组件只使用一次,则通常不需要“可复用”组件。

我们可以在另一个组件LoginPage中使用我们的输入组件。

class LoginPage extends React.Component {
  state = {
    user: {
      username: "",
      password: ""
    },
    errors: {},
    submitted: false
  };

  handleChange = event => {
    const { user } = this.state;
    user[event.target.name] = event.target.value;
    this.setState({ user });
  };

  onSubmit = () => {
    const {
      user: { username, password }
    } = this.state;
    let err = {};

    if (!username) {
      err.username = "Enter your username!";
    }

    if (password.length < 8) {
      err.password = "Password must be at least 8 characters!";
    }

    this.setState({ errors: err }, () => {
      if (Object.getOwnPropertyNames(this.state.errors).length === 0) {
        this.setState({ submitted: true });
      }
    });
  };

  render() {
    const {
      submitted,
      errors,
      user: { username, password }
    } = this.state;
    return (
      <React.Fragment>
        {submitted ? (
          <p>Welcome onboard, {username}!</p>
        ) : (
          <React.Fragment>
            <h3>Login!</h3>
            <FormInput
              label="Username"
              name="username"
              type="text"
              value={username}
              onChange={this.handleChange}
              placeholder="Enter username..."
              error={errors.username}
              required
              className="input"
            />

            <FormInput
              label="Password"
              name="password"
              type="password"
              value={password}
              onChange={this.handleChange}
              placeholder="Enter password..."
              error={errors.password}
              className="input"
              required
            />

            <Button
              type="submit"
              label="Submit"
              className="button"
              handleClick={this.onSubmit}
            />
          </React.Fragment>
        )}
      </React.Fragment>
    );
  }
}

看到LoginPage如何两次使用FormInput了吗?我们将其同时用作用户名和密码的文本输入。如果我们想更改输入的功能,我们可以在创建的FormInput组件文件中进行更改,这些更改将应用于使用输入组件的每个实例。这就是拥有可复用组件的基本优势:您无需重复自己。

甚至错误也是从FormInput组件中显示的。

onSubmit函数首先验证我们从表单中获取的user对象,确保它符合存在username值的结构。请注意,我们甚至可以扩展输入的功能,就像我们所做的那样,检查密码是否包含至少八个字符。

如果您查看代码,您会看到其中有一个Button组件。它与 HTML <button>元素不同,而是一个组件,它接收定义我们想要的按钮类型(提交、重置、按钮)、其类名、点击时要执行的操作以及标签的 props。有许多其他按钮属性我们可以集成以强制执行所需的任何标准。

const Button = (props) => (
  <button
    type={props.type}
    className={props.className}
    onClick={props.handleClick}
  >
    {props.label}
  </button>
)

这是将所有组件组合在一起后的最终登录表单。

查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 的笔
可复用按钮组件

CodePen 上。


想自己尝试一下吗?尝试开发一个可复用的<select>元素。如果这太难,您可以从<textarea>元素开始,然后是复选框,然后再跳到<select>。关键是要使其通用。我想看看您想出了什么,所以在评论区链接您的作品!