问题描述
我如何处理嵌套多步表单的状态、表单数据和逻辑,该表单深入三层,因为表单字段的输入会打开另一组完全不同的问题/字段。我是 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,但它需要更多的专业知识。