我创建了一个“ Tabbed Panels”(tabbed content)块,该块实际上只是一个InnerBlocks组件,仅允许该块“ Panel”。创建面板时,必须给面板指定一个标题,然后在面板以及“选项卡”按钮中使用该标题。因此,在用于选项卡式面板的渲染功能中,我需要从子面板面板中拉出标题。
[我可以使用几种方法,就像在tabbed-panels-render.php函数中使用regex来搜索子html的适当属性一样,但这似乎不是最好的方法。
我认为最简单的解决方案是侦听对Panel块的任何更改,并将更改(在这种情况下为标题和ID)保存到父级。我当前的方法基于this discussion,它使用钩子监听更改。该部分似乎工作正常,但我需要将输出保存在某处,以便将它们另存为“选项卡式面板”块的属性。乍看起来这似乎很好,但是将“ setAttributes”方法直接放在编辑函数中会导致问题。如果页面上的“选项卡式面板”块太多,那么React会引发“太多渲染”错误。
我的“ setAttributes”函数应该放在哪里,或者有更好的方法将数据从子代传递到父代?我曾考虑过在孩子中使用useDispatch钩子,但我需要检查很多事件(标题更改,代码块重新排序,代码块被删除等)
以下是相关的js和php文件。有一些自定义元素,但是您应该能够对其进行解析。
tabbed-panels.js
import { arraysMatch } from 'Components/utils.js'
const { InnerBlocks } = wp.blockEditor
const { createBlock } = wp.blocks
const { Button } = wp.components
const { useDispatch, useSelect } = wp.data
const { __ } = wp.i18n
export const tabbedPanels = {
name: 'my/tabbed-panels',
args: {
title: __('Tabbed Panels', '_ws'),
description: __('Tabbable panels of content.', '_ws'),
icon: 'table-row-after',
category: 'common',
supports: {
anchor: true
},
attributes: {
headings: {
type: 'array',
default: []
},
uids: {
type: 'array',
default: []
}
},
edit: props => {
const { setAttributes } = props
const { headings, uids } = props.attributes
const { insertBlock } = useDispatch('core/block-editor')
const { panelHeadings, panelUids, blockCount } = useSelect(select => {
const blocks = select('core/block-editor').getBlocks(props.clientId)
return {
panelHeadings: blocks.map(b => b.attributes.heading),
panelUids: blocks.map(b => b.clientId),
blockCount: select('core/block-editor').getBlockOrder(props.clientId).length
}
})
if (!arraysMatch(panelHeadings, headings)) {
setAttributes({ headings: panelHeadings })
}
if (!arraysMatch(panelUids, uids)) {
setAttributes({ uids: panelUids })
}
return (
<div className="block-row">
<InnerBlocks
allowedBlocks={ ['my/panel'] }
templateLock={ false }
renderAppender={ () => (
<Button
isSecondary
onClick={ e => {
insertBlock(createBlock('my/panel'), blockCount, props.clientId)
} }
>
{ __('Add Panel', '_ws') }
</Button>
) }
/>
</div>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
tabbed-panels-render.php
<?php
function block_tabbed_panels($atts, $content) {
$atts['className'] = 'wp-block-ws-tabbed-panels ' . ($atts['className'] ?? '');
$headings = $atts['headings'] ?? '';
$uids = $atts['uids'] ?? '';
ob_start(); ?>
<div class="tabs" role="tablist">
<?php
foreach ($headings as $i=>$heading) : ?>
<button
id="tab-<?= $uids[$i]; ?>"
class="tab"
role="tab"
aria-selected="false"
aria-controls="panel-<?= $uids[$i]; ?>"
tabindex="-1"
>
<?= $heading; ?>
</button>
<?php
endforeach; ?>
</div>
<div class="panels">
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
panel.js
import ComponentHooks from 'Components/component-hooks.js'
const { InnerBlocks, RichText } = wp.blockEditor
const { __ } = wp.i18n
export const panel = {
name: 'my/panel',
args: {
title: __('Panel', '_ws'),
description: __('Panel with associated tab.', '_ws'),
icon: 'format-aside',
category: 'common',
supports: {
customClassName: false,
html: false,
inserter: false,
reusable: false
},
attributes: {
heading: {
type: 'string'
},
uid: {
type: 'string'
}
},
edit: props => {
const { setAttributes } = props
const { heading } = props.attributes
return (
<>
<ComponentHooks
componentDidMount={ () => setAttributes({ uid: props.clientId }) }
/>
<RichText
label={ __('Tab Name', '_ws') }
placeholder={ __('Tab Name', '_ws') }
tagName="h4"
onChange={ newValue => setAttributes({ heading: newValue }) }
value={ heading }
/>
<InnerBlocks
templateLock={ false }
/>
</>
)
},
save: props => {
return (
<InnerBlocks.Content />
)
}
}
}
panel-render.php
<?php
function block_panel($atts, $content) {
$uid = $atts['uid'] ?? '';
ob_start(); ?>
<div
id="panel-<?= $uid ?>"
class="panel"
role="tabpanel"
aria-labelledby="tab-<?= $uid; ?>"
tabindex="0"
hidden="hidden"
>
<?= $content; ?>
</div>
<?php
return ob_get_clean();
}
您可以从父级访问子级并获取属性(在您的情况下为标签标题)。
componentDidUpdate(previousProps, previousState) {
var myID = this.props.clientId;
var tabs_title = [];
this.myBlock = wp.data.select('core/block-editor').getBlock(myID);
this.myBlock.innerBlocks.map(block => {
tabs_title.push( block.attributes.title );
});
this.props.setAttributes({ 'tabs_title': tabs_title });
}