gpt4 book ai didi

javascript - 我可以在 React 中跨多个树共享一个组件实例吗?

转载 作者:行者123 更新时间:2023-11-29 22:53:16 24 4
gpt4 key购买 nike

我一直在创建一个 API 来帮助管理 React 中的状态机。

它由三个部分组成:

  1. <StateMachine> : 收到 xstate 机器作为 Prop ,并为更深层次的组件使用设置上下文。
  2. <StateView> :接收两个 Prop :state & children ,并仅在该状态当前处于事件状态时呈现其子项。
  3. <StateControl> : 接收一些任意的 Prop - 每个 Prop 都是一个用于转换机器的事件 - 并将它们转换为转换回调以传递给它的 children (这不是一个元素,而是一个由 elementType 确定的 PropTypes)。

这是正在发生的事情的直观表示:

State Machine API Visual Diagram

使用 React 的上下文 API,我可以根据机器的状态灵活地打开/关闭 React 树中的节点。下面是演示这一点的示例代码片段:

const MyMachine = () => 
{
return (
<StateMachine machine={sampleMachine}>
<StateView state="initializing">
<StateControl onSuccess="success">
{MySampleInitializer}
</StateControl>
</StateView>
<StateView state="initialized">
<p>{"App initialized"}</p>
</StateView>
</StateMachine>
);

这很好用!当机器处于“正在初始化”状态时,MySampleInitializer被渲染。初始化完成后,onSuccess被称为将机器转换为“已初始化”。此时,<p>被渲染。

现在的问题:

在大多数情况下,每个“状态 View ”都会呈现一个不同的组件(当适当的状态变为事件状态时创建并安装)。

但是,如果我们只想将机器应用于单个组件怎么办?例如,我有一个 <Form>处理某些表单元素呈现的组件,并且应该根据表单当前所处的状态接收不同的属性

const MyFormMachine = () => 
{
return (
<StateMachine machine={formMachine}>
<StateView state="unfilled">
<StateControl onFill="filled">
{(props) => <MyForm {...props} disableSubmit/>}
</StateControl>
</StateView>
<StateView state="filled">
<StateControl onClear="unfilled" onSubmit="submit">
{(props) => <MyForm {...props}/>}
</StateControl>
</StateView>
<StateView state="submitting">
<MyForm disableInput disableSubmit showSpinner/>
</StateView>
</StateMachine>
);

使用我当前的 API,渲染 <MyForm>在每个 <StateView> 内会导致<MyForm>在状态发生变化的任何时候重新安装(从而破坏与之相关的任何内部状态)。 DOM 节点本身也将被重新挂载,这可能会重新触发类似 autofocus 的事情。 (例如)。

我希望有办法共享相同的 <MyForm>跨越各种“ View ”的实例,这样就不会发生这种重新安装。这可能吗?如果没有,是否有适合此 API 的替代解决方案?

非常感谢任何帮助。

PS:如果问题标题不合适,请提出更改建议,以便更容易理解此问题。谢谢

最佳答案

问题在于无论您是否处于正确的状态,您的 StateView 实例中的组件总是被构建。

因此在您的表单示例中,您总是有 3 个 Form 实例,但一次只呈现 1 个。如您所述,您只能拥有 1 个 Form 实例,以保持状态并防止重新安装。

当您将条件组件 (MyForm) 传递到另一个组件 (StateView) 时,您应该始终将其包装在一个函数中。

如果您处于正确的状态,您的 StateView 类只能创建 MyForm 实例。

您现在一次只有 1 个实例(假设一次只有 1 个状态匹配),但每个 StateView 仍然有自己的实例,该实例不共享。

据我所知,除非在同一个父组件中,否则您无法避免单独的实例。

我会更改您的 StateView 组件以处理多个状态检查,而不是一个。这样,当状态发生变化时,您的实例将被重用(再次假设一次只匹配 1 个状态)。

您的 StateView 结构可能如下所示:

<StateMachine machine={formMachine}>
<StateView>
{{
"unfilled": () => (
<StateControl onFill="filled">
{(props) => <MyForm {...props} disableSubmit/>}
</StateControl>
),
"filled": () => (
<StateControl onClear="unfilled" onSubmit="submit">
{(props) => <MyForm {...props}/>}
</StateControl>
),
"submitting": () => (
<StateControl>
{(props) => <MyForm disableInput disableSubmit showSpinner/>}
</StateControl>
)
}}
</StateView>
</StateMachine>

请注意,为了重用一个组件,该组件必须是同一类型。我将第 3 个 MyForm 包装在一个空的 StateControl 中,这样每个状态都构造一个 StateControl,因此组件可以重用。

另请注意,如果您确实在单个 StateView 中同时匹配多个状态,则可以为每个 StateControl 赋予一个 key 属性.不应在可能同时实例化的两个组件上使用相同的 key 属性值。一个更简单的解决方案是只拥有单独的 StateView 实例,其中每个实例一次只匹配一个状态。

关于javascript - 我可以在 React 中跨多个树共享一个组件实例吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57074298/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com