Gutenberg区块更新/发布后Toggle Control功能失效
Gutenberg区块Toggle Control保存后失效问题
功能正常场景
- 添加新页面时
- 添加新区块时
- 开启Toggle更改区块布局时
- 保存后区块在编辑器和前端的外观均正常变化
功能失效场景
- 编辑已保存的同一区块时,Toggle显示为关闭状态,但之前的布局更改已保存到前端
- 尝试再次切换Toggle开关时,无任何反应
相关代码
edit.js
import { __ } from '@wordpress/i18n'; import { RichText, InspectorControls, MediaUpload, useBlockProps, InnerBlocks, } from '@wordpress/block-editor'; import { PanelBody, Button, ToggleControl } from '@wordpress/components'; const ALLOWED_BLOCKS = ['core/button']; import './editor.scss'; const Edit = ({ attributes, setAttributes }) => { const { toggleOn, customClass, align } = attributes; const onToggleChange = (newValue) => { setAttributes({ toggleOn: newValue }); }; const onSelectBlockBackground = (newBlockBackground) => { setAttributes({ blockBackground: newBlockBackground.sizes.full.url, }); }; const blockProps = useBlockProps({ className: `wp-block-create-block-callout-block ${customClass} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`, 'data-align': align, }); return ( <div {...blockProps}> <InspectorControls> <PanelBody title={__('Toggle on horizontal or vertical layout')}> <ToggleControl label={__('Toggle On')} checked={toggleOn} onChange={onToggleChange} /> </PanelBody> <PanelBody title={__('Select Background Image')}> <p> <strong>{__('Select a Background Image:')}</strong> </p> <MediaUpload onSelect={onSelectBlockBackground} type="image" value={attributes.blockBackground.url} render={({ open }) => ( <Button className="editor-media-placeholder__button is-button is-default is-large" icon="upload" onClick={open} > {__('Select Image')} </Button> )} /> </PanelBody> </InspectorControls> <div className={`callout-cta`} style={{ backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none', backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', }} > <RichText tagName="h2" value={attributes.mainHeading} onChange={(mainHeading) => setAttributes({ mainHeading })} placeholder={__('Main Heading')} className="main-heading" /> <RichText tagName="p" value={attributes.mainContent} onChange={(mainContent) => setAttributes({ mainContent })} placeholder={__('Main Content')} className="main-content" /> <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} /> </div> </div> ); }; export default Edit;
index.js
import { registerBlockType } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { RichText, InspectorControls, MediaUpload, useBlockProps } from '@wordpress/block-editor'; import { PanelBody, Button } from '@wordpress/components'; import './style.scss'; import Edit from './edit'; import save from './save'; const validAlignments = ['full']; registerBlockType('create-block/callout-block', { title: __('Callout Block', 'callout-block'), supports: { html: false, align: true, }, attributes: { mainHeading: { type: 'string', source: 'html', selector: '.main-heading', }, mainContent: { type: 'string', source: 'html', selector: '.main-content', }, blockBackground: { type: 'string', default: '', }, align: { type: 'string', default: 'full', }, toggleOn: { type: 'boolean', default: false, source: 'attribute', attribute: 'data-toggle-on', // Use the attribute name that corresponds to the class you're toggling }, }, getEditWrapperProps( attributes ) { const { align } = attributes; if (-1 !== validAlignments.indexOf( align ) ) { return { 'data-align': align }; } }, edit: Edit, save, });
save.js
import { __ } from '@wordpress/i18n'; import { RichText, InspectorControls, MediaUpload, useBlockProps, InnerBlocks, } from '@wordpress/block-editor'; import { PanelBody, Button } from '@wordpress/components'; export default function save({ className, attributes }) { const { toggleOn } = attributes; const blockProps = useBlockProps.save({ 'data-align': attributes.align, // Add the data-align attribute with the align value className: `wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`, }); return ( <div className={`wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`} {...blockProps}> <div className="callout-cta" style={{ backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none', backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', }} > <RichText.Content tagName="h2" value={attributes.mainHeading} className="main-heading" /> <RichText.Content tagName="p" value={attributes.mainContent} className="main-content" /> <InnerBlocks.Content {...blockProps} /> </div> </div> ); }
问题根源
问题出在toggleOn属性的配置上:
- 在
index.js中给toggleOn设置了source: 'attribute'和attribute: 'data-toggle-on',意味着Gutenberg会从保存的HTML元素的data-toggle-on属性中读取该值,但save.js里并没有在根元素上添加这个属性,导致编辑器重新加载时无法读取到之前保存的toggleOn值,只能使用默认的false。 - 同时,由于
source设置为attribute,当尝试通过setAttributes更新toggleOn时,Gutenberg会试图同步到DOM的对应属性,但DOM中不存在该属性,导致更新失效,开关无法正常切换。
解决方案
1. 修改index.js中的toggleOn属性定义
移除source和attribute配置,让Gutenberg将该属性存储在区块内部数据中(而非从HTML提取):
toggleOn: { type: 'boolean', default: false, // 移除source和attribute配置 },
2. 优化save.js的代码
- 移除重复的className定义(
useBlockProps.save会自动添加区块默认className) - 简化
InnerBlocks.Content的调用(不需要传递blockProps):
import { __ } from '@wordpress/i18n'; import { RichText, useBlockProps, InnerBlocks, } from '@wordpress/block-editor'; export default function save({ attributes }) { const { toggleOn } = attributes; const blockProps = useBlockProps.save({ 'data-align': attributes.align, className: toggleOn ? 'toggle-on-horizontal-cta' : '', }); return ( <div {...blockProps}> <div className="callout-cta" style={{ backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none', backgroundSize: 'cover', backgroundPosition: 'center', backgroundRepeat: 'no-repeat', }} > <RichText.Content tagName="h2" value={attributes.mainHeading} className="main-heading" /> <RichText.Content tagName="p" value={attributes.mainContent} className="main-content" /> <InnerBlocks.Content /> </div> </div> ); }
3. 优化edit.js中的blockProps
移除手动添加的区块默认className(useBlockProps会自动处理):
const blockProps = useBlockProps({ className: `${customClass} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`, 'data-align': align, });
效果验证
修改后重新保存区块,再次编辑时:
- Toggle会正确显示之前保存的状态
- 开关可以正常切换,布局会实时更新,且保存后状态不会丢失
内容的提问来源于stack exchange,提问作者Chris Brennan
相关产品推荐
相关产品推荐

