在 React 中,复合组件允许您创建具有某种形式的连接状态的组件,这些状态由组件本身管理。 一个很好的例子是 表单 组件,它在 Semantic UI React 中。
为了了解如何在真实的 React 应用程序中实现复合组件,我们将构建一个用于登录和注册的复合(多部分)表单。 状态将保存在表单组件中,我们将使用 React 的 Context API 来传递该状态和 Context Provider 中的方法到需要它们的组件。 哪些组件需要它们? 它将成为 Context Consumers 的订阅者。
以下是我们正在构建的内容
查看 CodePen 上 Kingsley Silas Chijioke(@kinsomicrote)的 React 复合组件。
以下是一个粗略的概述,显示了以下步骤是如何组合在一起的

在进一步了解之前,您可能需要先学习 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>
);
};
您可以看到 activePanel
与 isActive
(应该返回 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() 创建复合组件。
很高兴我的文章能帮到您
我有一个问题:将最后两个组件包装到 React.Fragment 中的原因是什么?
这让我很困惑,我在想我是否漏掉了什么?
你好 Vladyslav,我不确定你指的是哪一部分。