古腾堡编辑器自定义区块:如何仅在创建时设置postDate
问题
我正在为WordPress古腾堡编辑器开发一款自定义区块,用于展示并保存内容与日期。原本打算通过postDate属性保存每个区块的创建日期,虽然能通过save函数成功保存,但所有区块的postDate都是第一个区块的创建日期,无法随新区块更新。
需求是:仅在区块创建时设置一次postDate,后续编辑不可修改该值。工作流程为用户创建区块时保存postDate;创建新区块时重复流程,每个区块拥有唯一日期。
之前尝试在useEffect的!blockId条件中设置postDate,但会导致每次渲染都更新值,请问应该在何处调用setAttribute设置postDate以实现仅创建时更新?
原代码如下:
registerBlockType(metadata.name, { attributes: { content: { type: "string", source: "html", selector: "h2", }, postDate: { type: "string", default: moment().tz("Europe/Paris").locale("fr").format(), }, blockId: { type: "string", }, }, edit({ attributes, setAttributes, clientId }) { const blockProps = useBlockProps(); const { postDate, blockId } = attributes; React.useEffect(() => { if (!blockId) { setAttributes({ blockId: clientId }); } }, []); const TEMPLATE = [["core/paragraph", {}]]; const ALLOWED_BLOCKS = useMemo( () => wp.blocks .getBlockTypes() .filter((block) => block.name !== metadata.name) .map((block) => block.name), [metadata.name] ); return ( <div className="wp-post-live-block"> <time dateTime={postDate}> {moment(postDate).format("D/M H:mm:ss")} </time> <RichText {...blockProps} tagName="h2" value={attributes.content} onChange={(content) => setAttributes({ content })} placeholder={__("Add title")} /> <InnerBlocks template={TEMPLATE} allowedBlocks={ALLOWED_BLOCKS} /> </div> ); }, save({ attributes }) { const blockProps = useBlockProps.save(); const { postDate, blockId } = attributes; return ( <div data-timestamp={postDate} id={blockId} className="wp-post-live-block"> <RichText.Content {...blockProps} tagName="h2" value={attributes.content} /> <InnerBlocks.Content /> </div> ); }, });
解决方案
核心问题分析
你当前的postDate属性在定义时设置了default值,这个值是在区块注册阶段(registerBlockType执行时)就计算完成的,而不是每个区块实例创建时动态生成。所以所有新创建的区块都会复用这个初始计算的日期,导致所有区块日期一致。
实现步骤
- 移除
postDate的默认值:将postDate的default配置删除,让其默认值为undefined,这样可以区分区块是新创建的还是已存在的。 - 在
useEffect中判断并设置日期:结合postDate是否存在以及blockId的初始化逻辑,确保只在区块首次创建时设置一次日期。同时要正确设置useEffect的依赖项,避免重复执行。
修改后的代码
registerBlockType(metadata.name, { attributes: { content: { type: "string", source: "html", selector: "h2", }, postDate: { type: "string", // 移除默认值,让新创建的区块postDate为undefined }, blockId: { type: "string", }, }, edit({ attributes, setAttributes, clientId }) { const blockProps = useBlockProps(); const { postDate, blockId } = attributes; React.useEffect(() => { // 仅当区块是新创建(无postDate且无blockId)时执行 if (!blockId && !postDate) { setAttributes({ blockId: clientId, postDate: moment().tz("Europe/Paris").locale("fr").format() }); } }, [blockId, postDate, clientId, setAttributes]); // 添加必要依赖项 const TEMPLATE = [["core/paragraph", {}]]; const ALLOWED_BLOCKS = useMemo( () => wp.blocks .getBlockTypes() .filter((block) => block.name !== metadata.name) .map((block) => block.name), [metadata.name] ); return ( <div className="wp-post-live-block"> <time dateTime={postDate}> {moment(postDate).format("D/M H:mm:ss")} </time> <RichText {...blockProps} tagName="h2" value={attributes.content} onChange={(content) => setAttributes({ content })} placeholder={__("Add title")} /> <InnerBlocks template={TEMPLATE} allowedBlocks={ALLOWED_BLOCKS} /> </div> ); }, save({ attributes }) { const blockProps = useBlockProps.save(); const { postDate, blockId } = attributes; return ( <div data-timestamp={postDate} id={blockId} className="wp-post-live-block"> <RichText.Content {...blockProps} tagName="h2" value={attributes.content} /> <InnerBlocks.Content /> </div> ); }, });
额外说明
- 依赖项中加入
blockId、postDate、clientId和setAttributes,确保useEffect仅在这些值变化时触发,避免不必要的渲染。 - 同时判断
!blockId和!postDate,确保只有全新的区块才会设置日期,已保存过的区块不会被修改。
内容的提问来源于stack exchange,提问作者MrFacundo
相关产品推荐
相关产品推荐

