You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WordPress Gutenberg可编辑Quiz重复字段实现方案求助

问题描述

在WordPress Gutenberg中开发Quiz区块时,尝试通过useState生成重复的<QuizWrapper>组件,点击按钮可新增组件,但重复后的输入框无法编辑。单独使用<QuizWrapper>时编辑功能正常,批量生成的组件仅能显示,无法交互。

问题根源
  1. 共享属性冲突:所有重复的<QuizWrapper>都复用同一个Block属性(question、answers等),修改其中一个会同步影响所有组件,且渲染时无法区分不同项的独立状态。
  2. 错误的组件存储方式:用useState直接存储React元素,导致组件无法响应属性更新,失去编辑交互能力。
  3. 数据结构不支持多Quiz项:当前Block的Attributes设计为单个Quiz的字段,未适配多Quiz项的存储需求。
解决方案

1. 重构Block Attributes

修改index.js,将单个Quiz字段改为数组类型,用于存储多个独立Quiz项的数据:

import { registerBlockType } from "@wordpress/blocks";
import "./style.scss";
import Edit from "./edit";
import save from "./save";
import metadata from "./block.json";

registerBlockType(metadata.name, {
    attributes: {
        quizzes: { 
            type: "array", 
            default: [
                {
                    question: "Example Question",
                    answers: [""],
                    correctAnswer: undefined,
                    bgColor: "#ededed"
                }
            ] 
        },
    },
    edit: Edit,
    save,
});

2. 修改Edit组件逻辑

不再用useState存储组件,而是基于quizzes属性动态渲染组件,新增/删除Quiz项时直接更新Block属性:

import { __ } from "@wordpress/i18n";
import { Button } from "@wordpress/components";
import { useBlockProps } from "@wordpress/block-editor";
import "./editor.scss";
import QuizWrapper from "./components/QuizWrapper";

export default function Edit(props) {
    const { attributes, setAttributes } = props;
    const blockProps = useBlockProps({ className: "quiz-wrapper" });

    // 新增Quiz项
    const addSection = () => {
        setAttributes({
            quizzes: [
                ...attributes.quizzes,
                {
                    question: "New Question",
                    answers: [""],
                    correctAnswer: undefined,
                    bgColor: "#ededed"
                }
            ]
        });
    };

    // 删除指定索引的Quiz项
    const deleteQuiz = (index) => {
        const newQuizzes = attributes.quizzes.filter((_, i) => i !== index);
        setAttributes({ quizzes: newQuizzes });
    };

    return (
        <div {...blockProps}>
            {attributes.quizzes.map((quiz, index) => (
                <QuizWrapper 
                    key={index} 
                    quiz={quiz} 
                    index={index} 
                    setAttributes={setAttributes} 
                    quizzes={attributes.quizzes}
                    deleteQuiz={() => deleteQuiz(index)}
                />
            ))}
            <Button isPrimary onClick={addSection}>
                Add Another Section
            </Button>
        </div>
    );
}

3. 更新QuizWrapper组件

接收单个Quiz数据和索引,传递给子组件并添加删除功能:

import React from 'react'
import Question from "./Question"
import Answers from "./Answers"
import { Button } from "@wordpress/components";

const QuizWrapper = ({ quiz, index, setAttributes, quizzes, deleteQuiz }) => {
    return (
        <div className='quizes'>
            <Question 
                quiz={quiz} 
                index={index} 
                setAttributes={setAttributes} 
                quizzes={quizzes}
            />
            <Answers 
                quiz={quiz} 
                index={index} 
                setAttributes={setAttributes} 
                quizzes={quizzes}
            />
            {quizzes.length > 1 && (
                <Button isDestructive onClick={deleteQuiz} style={{ marginTop: '16px' }}>
                    Delete Quiz
                </Button>
            )}
        </div>
    )
}

export default QuizWrapper

4. 修改Question组件

操作对应索引的Quiz数据,实现独立编辑:

import { TextControl } from "@wordpress/components";

export default function Question({ quiz, index, setAttributes, quizzes }) {
    const updateQuestion = (value) => {
        const newQuizzes = [...quizzes];
        newQuizzes[index].question = value;
        setAttributes({ quizzes: newQuizzes });
    };

    return (
        <div className="question">
            <h2 style={{ fontSize: "20px", margin: "20px 0 8px 0" }}>Questions: </h2>
            <TextControl
                value={quiz.question}
                onChange={updateQuestion}
            />
        </div>
    );
}

5. 更新Answers组件

操作对应索引的Quiz的答案数据,维护独立的答案状态:

import { TextControl, Button, Icon } from "@wordpress/components";

export default function Answers({ quiz, index, setAttributes, quizzes }) {
    const markAsCorrect = (answerIndex) => {
        const newQuizzes = [...quizzes];
        newQuizzes[index].correctAnswer = answerIndex;
        setAttributes({ quizzes: newQuizzes });
    };

    const deleteAnswer = (answerIndexToDelete) => {
        const newQuizzes = [...quizzes];
        newQuizzes[index].answers = newQuizzes[index].answers.filter((_, i) => i !== answerIndexToDelete);
        
        // 删除正确答案时重置标记
        if (answerIndexToDelete === newQuizzes[index].correctAnswer) {
            newQuizzes[index].correctAnswer = undefined;
        }
        
        setAttributes({ quizzes: newQuizzes });
    };

    const addAnswer = () => {
        const newQuizzes = [...quizzes];
        newQuizzes[index].answers = [...newQuizzes[index].answers, ""];
        setAttributes({ quizzes: newQuizzes });
    };

    return (
        <div className="answers">
            <p style={{ fontSize: "13px", margin: "20px 0 8px 0" }}>Answers: </p>
            {quiz.answers.map((answer, answerIndex) => (
                <div className="answer-wrapper" key={answerIndex}>
                    <Button onClick={() => markAsCorrect(answerIndex)}>
                        <Icon
                            className="mark-as-correct"
                            icon={quiz.correctAnswer === answerIndex ? "yes-alt" : "yes"}
                        />
                    </Button>
                    <TextControl
                        value={answer}
                        onChange={(updatedAnswer) => {
                            const newQuizzes = [...quizzes];
                            newQuizzes[index].answers[answerIndex] = updatedAnswer;
                            setAttributes({ quizzes: newQuizzes });
                        }}
                    />
                    <Button
                        isLink
                        className="deleteBtn"
                        onClick={() => deleteAnswer(answerIndex)}
                    >
                        Delete
                    </Button>
                </div>
            ))}
            <Button isPrimary onClick={addAnswer}>
                Add Another Answer
            </Button>
        </div>
    );
}
关键说明
  • 所有Quiz项的数据都存储在Block的quizzes属性中,每个项独立维护自己的问题、答案等字段。
  • 通过索引区分不同的Quiz项,修改时仅更新对应索引的数据,彻底避免状态冲突。
  • 不再存储React元素,而是基于数据动态渲染组件,确保组件能响应属性更新,恢复编辑交互能力。

内容的提问来源于stack exchange,提问作者Farhan Mullick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:50:28