使用 Context API 在 React 中创建复合组件

Avatar of Kingsley Silas
Kingsley Silas

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

在 React 中,复合组件允许您创建具有某种形式的连接状态的组件,这些状态由组件本身管理。 一个很好的例子是 表单 组件,它在 Semantic UI React 中。

为了了解如何在真实的 React 应用程序中实现复合组件,我们将构建一个用于登录和注册的复合(多部分)表单。 状态将保存在表单组件中,我们将使用 React 的 Context API 来传递该状态和 Context Provider 中的方法到需要它们的组件。 哪些组件需要它们? 它将成为 Context Consumers 的订阅者。

以下是我们正在构建的内容

查看 CodePen 上 Kingsley Silas Chijioke(@kinsomicrote)的 React 复合组件

以下是一个粗略的概述,显示了以下步骤是如何组合在一起的

Form is the provider with state, Form Panel is the consumer receiving state, Panel displays the panel based on the state, and Signup and Login render the form views in the Panel.

在进一步了解之前,您可能需要先学习 React Context API。 Neal Fennimore 在 这篇文章 中演示了该概念,而 我关于它的入门介绍 也值得一看。

步骤 1:创建 context

首先,让我们使用 React Context API 初始化一个新的 context。

const FormContext = React.createContext({});
const FormProvider = FormContext.Provider;
const FormConsumer = FormContext.Consumer;

提供者 FormProvider 将保存应用程序状态,使其可用于订阅 FormConsumer 的组件。

步骤 2:实现提供者

一个面板包含用于登录的表单,另一个面板包含用于注册的表单。 在提供者中,我们想要声明状态,该状态确定活动面板,即当前显示的表单。 我们还将创建一个方法,当点击标题时,从一个面板切换到另一个面板。

class Form extends React.Component {
  state = {
    activePanel: "login"
  };

  render() {
    return (
      <React.Fragment>
        <FormProvider
          value={{
            activePanel: this.state.activePanel,
            actions: {
              handlePanelSwitch: newPanel => {
                this.setState({
                  activePanel: newPanel
                });
              }
            }
          }}
        >
          {this.props.children}
        </FormProvider>
      </React.Fragment>
    );
  }
}

默认情况下,登录面板将显示给用户。 当点击注册面板时,我们想要通过将 activePanel 的状态设置为 signup,使用 handlePanelSwitch() 方法,使其成为活动面板。

步骤 3:实现 Consumers

我们将使用 FormConsumer 使 context 可用于订阅它的组件。 这意味着处理显示面板的 FormPanel 组件将如下所示

const FormPanel = props => {
  return (
    <FormConsumer>
      {({ activePanel }) =>
        activePanel === props.isActive ? props.children : null
      }
    </FormConsumer>
  );
};

Panel 组件将如下所示

const Panel = props => (
  <FormConsumer>
    {({ actions }) => {
      return (
        <div onClick={() => actions.switchPanel(props.id)}>
          {props.children}
        </div>
      );
    }}
  </FormConsumer>
);

为了理解这里发生了什么,让我们理解一下这里的方法。 登录和注册面板将具有唯一的 ID,这些 ID 通过 props 传递给 Panel 组件。 当选择一个面板时,我们获取 ID 并使用它将 activePanel 设置为交换表单。 FormPanel 组件还通过 isActive prop 接收面板的名称,然后我们检查返回的值是否为 true。 如果是,则渲染面板!

为了获取完整的 context,以下是 App 组件的外观

const App = () => {
  return (
    <div className="form-wrap">
      <Form>
        <div className="tabs">
          <Panel id="login">
            <h2 className="login-tab">Login</h2>
          </Panel>
          <Panel id="signup">
            <h2 className="signup-tab">Sign Up</h2>
          </Panel>
        </div>

        <FormPanel isActive="login">
          <Login />
        </FormPanel>

        <FormPanel isActive="signup">
          <SignUp />
        </FormPanel>
      </Form>
    </div>
  );
};

您可以看到 activePanelisActive(应该返回 true)匹配时,组件是如何组合的。 在这些条件下渲染组件。

完成之后,Login 组件如下所示

const Login = () => {
  return (
    <React.Fragment>
      <div id="login-tab-content">
        <form className="login-form" action="" method="post">
          <input
            type="text"
            className="input"
            id="user_login"
            placeholder="Email or Username"
          />
          <input
            type="password"
            className="input"
            id="user_pass"
            placeholder="Password"
          />
          <input type="checkbox" className="checkbox" id="remember_me" />
          <label htmlFor="remember_me">Remember me</label>

          <input type="submit" className="button" value="Login" />
        </form>
      </div>
    </React.Fragment>
  );
};

以及 SignUp 组件

const SignUp = () => {
  return (
    <React.Fragment>
      <div id="signup-tab-content" className="active tabs-content">
        <form className="signup-form" action="" method="post">
          <input
            type="email"
            className="input"
            id="user_email"
            placeholder="Email"
          />
          <input
            type="text"
            className="input"
            id="user_name"
            placeholder="Username"
          />
          <input
            type="password"
            className="input"
            id="user_pass"
            placeholder="Password"
          />
          <input type="submit" className="button" value="Sign Up" />
        </form>
      </div>
    </React.Fragment>
  );
};

明白了吗? 很好!

您可以在 React 应用程序中需要共享隐式状态的组件时,随时使用此模式。 您还可以使用 React.cloneElement() 创建复合组件

参考