How to output child block attributes on a parent block

It’s worth a mention that storing child block attributes in the parent block’s attributes is a bit of a bizarre practice in isolation – ideally blocks should be completely independent of one-another. But if some functionality necessitates that a parent block access a child block’s attributes, it would likely have a much smaller potential for headaches down the road to just read the attributes from the child blocks as necessary rather than duplicating the data into parent block attributes.

In short, you need to create a subscription which will re-render the component when changes occur in editor state by using either the useSelect() hook or the withSelect() HOC factory (I’ve written a brief introduction to the concepts behind these functions over here).

While a subscription to getBlocksByClientId( parent_block_client_id ) would be perfectly functional as the parent block’s state becomes “dirty” when any change is made is made to a child block, I think a getBlocks( parent_block_client_id ) selector is a little cleaner as you don’t have to traverse down to the innerBlocks array, and additionally the subscription won’t trigger a re-render when any modification is made to the parent block.

In order to reduce overhead, it would be a good practice to place your logic which updates your attributes in a useEffect() hook with a dependency on the child block list such that it only occurs when the child blocks change.

import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { useEffect } from '@wordpress/element';

const Edit = ( { attributes, clientId, setAttributes, ...props } ) => {
  const { child_values } = attributes;
  const child_blocks = useSelect(
    select => select( 'core/block-editor' ).getBlocks( clientId )
  );

  useEffect(
    () => {
      const child_values = child_blocks.map(
        ( { clientId, attributes: { title } } ) => ( { clientId, title } )
      );
      setAttributes( { child_values } );
    },
    [ child_blocks ]
  );

  return (
    <div {...useBlockProps()}>
      <div className="block-content">
        <InnerBlocks />
      </div>
      <aside>
      {child_values.map( ( { clientId, title }, index ) => (
        <p id={ clientId } key={ index }>{title}</p>
      ))}
      </aside>
    </div>
  );
}

export default Edit;