如何在 React 中处理 3 层深度嵌套的多步表单?

问题描述

我如何处理嵌套多步表单的状态、表单数据和逻辑,该表单深入三层,因为表单字段的输入会打开另一组完全不同的问题/字段。我是 React 新手,我看到的所有教程都只处理了三个步骤,只有文本字段,没有单选按钮。

我如何准确地嵌套步骤,同时仍然保持逻辑而不使一切变得复杂。

就上下文而言,我打算构建的表单包含六种全球产品的列表,您只能从中选择一种。 这六个全局选项具有相同的三个订阅层。订阅层 1 有它的一组表单字段可以显示订阅层 2 和订阅层 3 也是如此。

进入订阅层后,将分为三个阶段,包括产品详细信息、客户信息和最后阶段(其中包含一个页面,该页面列出了用户填写的所有表单字段和数据,还包含一个按钮进入支付页面,这是最后阶段)。

在应用程序显示要填充的当前字段的每个页面上,还有一个部分显示一个字段和用户数据,用户可以点击这些数据返回编辑特定点击的字段。

页面还包含两个步进器,用于突出显示每个页面一个阶段中的当前问题编号,另一个步进器突出显示所有阶段(1、2、3)中的当前活动阶段,因为我们只有三个阶段。

因此,如果用户在第 2 阶段的问题 5 上。数字 5 将在问题数字步进器上突出显示,而 2 也将在舞台步进器上突出显示。这些步进器的数字只有在它们被填充后才能被点击,这应该会将我们带到确切的字段和在那里填充的数据进行编辑。用户可以随时更改阶段和问题编号,前提是它们仅被填写。

我非常感谢任何以代码架构和/或解释或资源形式提供的指导。

以下是提供三个步骤的此类教程参考点链接的示例:https://css-tricks.com/the-magic-of-react-based-multi-step-forms/

谢谢大家

解决方法

不幸的是,表单很快就会变得非常棘手。根据我的经验,如果您要使用表单捕获一组非常复杂的数据;您将需要一些非常好的抽象和工具来处理它。

首先要考虑的是您是否可以简化问题以允许自己创建更简单的表单。例如,如果您可以让您的表单更纯粹,这意味着与外部世界的互动更少,或者您可以控制自己的行为方式,那么您可以尝试一些简单的抽象。我最喜欢的是使用 JavaScript 生成器为顺序渲染创建类似“传奇”的语法。

这是一个例子:

import React from "react";
import { render } from "react-dom";
import { Tale } from "../../react-tale";

const ChooseRocket = ({ launch }) => (
  <div>
    <p>Mission control here,what rocket are we launching today?</p>
    <button onClick={() => launch("red")}>Red Rocket</button>
    <button onClick={() => launch("green")}>Green Rocket</button>
    <button onClick={() => launch("blue")}>Blue Rocket</button>
  </div>
);

const CheckItem = ({ name,value,check }) => (
  <div>
    <input
      name={name}
      type="checkbox"
      value={value}
      onChange={(e) => check({ name,status: e.target.checked })}
    />
    <label htmlFor={name}>{name}</label>
  </div>
);

const CheckList = ({ list,check }) => (
  <div>
    <p>Lets run through safety procedures...</p>
    {list.map(({ name,status }) => (
      <CheckItem key={name} name={name} value={status} check={check} />
    ))}
  </div>
);

const GoNoGo = ({ go,nogo }) => (
  <div>
    <p>All checks ready,we have GO or NO-GO,what is your call?</p>
    <button onClick={nogo}>We are NO-GO</button>
    <button onClick={go}>We are a GO</button>
  </div>
);

function* launchProtocol() {
  const rocket = yield (next) => <ChooseRocket launch={next} />;

  let list = [
    { name: "BOOSTER",status: false },{ name: "RETRO",{ name: "FIDO",];

  while (!list.every((check) => check.status)) {
    list = yield (next) => (
      <CheckList
        list={list}
        check={(x) => next(list.map((y) => (x.name === y.name ? x : y)))}
      />
    );
  }

  const going = yield (next) => (
    <GoNoGo go={() => next(true)} nogo={() => next(false)} />
  );

  if (going) {
    return `There goes the ${rocket} rocket!`;
  } else {
    return "Rescheduling launch to CTRL+R";
  }
}

const mount = document.getElementById("app");
if (mount) render(<Tale tale={launchProtocol} />,mount);

或类似的东西:

import React from "react";
import { render,createPortal } from "react-dom";
import { Tale } from "../../react-tale";

const CallToAction = ({ onClick }) => (
  <div>
    Want to get in on something really cool?
    <button onClick={onClick}>Yes!</button>
  </div>
);

const Waiting = () => <div>Wainting for your answer!</div>;

const ConfirmationDialog = ({ yes,no }) => (
  <div>
    Are you sure about that?
    <button onClick={yes}>Yes!</button>
    <button onClick={no}>No</button>
  </div>
);

const Modal = ({ children }) =>
  createPortal(children,document.getElementById("modal"));

function* confirming() {
  yield (next) => <CallToAction onClick={next} />;

  const confirmation = yield (next) => (
    <div>
      <Waiting />
      <Modal>
        <ConfirmationDialog yes={() => next(true)} no={() => next(false)} />
      </Modal>
    </div>
  );

  if (confirmation) {
    return "Doing some cool stuff!";
  } else {
    yield (next) => (
      <div>
        That is okay,if you change your mind let me know.
        <button onClick={next}>I changed my mind!</button>
      </div>
    );
    yield* confirming();
  }
}

const mount = document.getElementById("app");
if (mount) render(<Tale tale={confirming} />,mount);

<Tale /> 组件如下:

import { useMemo,useState } from "react";

export function Tale({ tale }) {
  const G = useMemo(tale,[tale]);
  const [{ value,done },setStep] = useState(() => G.next());
  if (done) return value;
  return typeof value === "function" ? value((e) => setStep(G.next(e))) : value;
}

现在的关键是,如果您现在想在这些步骤之间访问状态或进行 API 调用,您必须以某种方式将如何做这些事情的解释烘焙到 Tale 组件中。所以你基本上会开始创建你自己的小表单抽象。如果您想要非常细粒度地​​访问表单的顺序渲染,这可能是值得的。而且这一切都基于生成器,这可能会让你觉得它很复杂,或者让你看到所有的可能性;这取决于程序员。

您可以研究一些非常好的 Form 抽象,例如 Formlets,但它需要更多的专业知识。