diff --git a/packages/block-library/src/block/edit.js b/packages/block-library/src/block/edit.js
index 296fb704dbfa7e..8d65ef2bab45ac 100644
--- a/packages/block-library/src/block/edit.js
+++ b/packages/block-library/src/block/edit.js
@@ -6,207 +6,158 @@ import { partial } from 'lodash';
/**
* WordPress dependencies
*/
-import { Component } from '@wordpress/element';
-import { Placeholder, Spinner, Disabled } from '@wordpress/components';
-import { withSelect, withDispatch } from '@wordpress/data';
-import { __ } from '@wordpress/i18n';
import {
BlockEditorProvider,
BlockList,
WritingFlow,
} from '@wordpress/block-editor';
-import { compose } from '@wordpress/compose';
import { parse, serialize } from '@wordpress/blocks';
+import { Placeholder, Spinner, Disabled } from '@wordpress/components';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { useEffect, useState } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import ReusableBlockEditPanel from './edit-panel';
-class ReusableBlockEdit extends Component {
- constructor( { reusableBlock } ) {
- super( ...arguments );
-
- this.startEditing = this.startEditing.bind( this );
- this.stopEditing = this.stopEditing.bind( this );
- this.setBlocks = this.setBlocks.bind( this );
- this.setTitle = this.setTitle.bind( this );
- this.save = this.save.bind( this );
-
- if ( reusableBlock ) {
- // Start in edit mode when we're working with a newly created reusable block
- this.state = {
- isEditing: reusableBlock.isTemporary,
- title: reusableBlock.title,
- blocks: parse( reusableBlock.content ),
- };
- } else {
- // Start in preview mode when we're working with an existing reusable block
- this.state = {
- isEditing: false,
- title: null,
- blocks: [],
+export default function ReusableBlockEdit( { attributes, isSelected } ) {
+ const { ref } = attributes;
+
+ const {
+ reusableBlock,
+ isFetching,
+ isSaving,
+ isTemporary,
+ blocks,
+ canUpdateBlock,
+ settings,
+ title,
+ } = useSelect(
+ ( select ) => {
+ const { canUser } = select( 'core' );
+ const { getSettings } = select( 'core/block-editor' );
+ const {
+ __experimentalGetReusableBlock: getReusableBlock,
+ __experimentalIsFetchingReusableBlock: isFetchingReusableBlock,
+ __experimentalIsSavingReusableBlock: isSavingReusableBlock,
+ } = select( 'core/editor' );
+ const _reusableBlock = getReusableBlock( ref );
+
+ return {
+ reusableBlock: _reusableBlock,
+ isFetching: isFetchingReusableBlock( ref ),
+ isSaving: isSavingReusableBlock( ref ),
+ isTemporary: _reusableBlock?.isTemporary ?? null,
+ blocks: _reusableBlock ? parse( _reusableBlock.content ) : null,
+ canUpdateBlock:
+ !! _reusableBlock &&
+ ! _reusableBlock.isTemporary &&
+ !! canUser( 'update', 'blocks', ref ),
+ settings: getSettings(),
+ title: _reusableBlock?.title ?? null,
};
- }
- }
+ },
+ [ ref ]
+ );
- componentDidMount() {
- if ( ! this.props.reusableBlock ) {
- this.props.fetchReusableBlock();
- }
- }
+ const {
+ __experimentalFetchReusableBlocks: fetchReusableBlocks,
+ __experimentalUpdateReusableBlock: updateReusableBlock,
+ __experimentalSaveReusableBlock: saveReusableBlock,
+ } = useDispatch( 'core/editor' );
- componentDidUpdate( prevProps ) {
- if (
- prevProps.reusableBlock !== this.props.reusableBlock &&
- this.state.title === null
- ) {
- this.setState( {
- title: this.props.reusableBlock.title,
- blocks: parse( this.props.reusableBlock.content ),
- } );
- }
- }
+ const fetchReusableBlock = partial( fetchReusableBlocks, ref );
+ const onChange = partial( updateReusableBlock, ref );
+ const onSave = partial( saveReusableBlock, ref );
- startEditing() {
- const { reusableBlock } = this.props;
- this.setState( {
- isEditing: true,
- title: reusableBlock.title,
- blocks: parse( reusableBlock.content ),
- } );
- }
+ // Start in edit mode when working with a newly created reusable block.
+ // Start in preview mode when we're working with an existing reusable block.
+ const [ isEditing, setIsEditing ] = useState( isTemporary ?? false );
- stopEditing() {
- this.setState( {
- isEditing: false,
- title: null,
- blocks: [],
- } );
- }
+ // Local state so changes can be made to the reusable block without having to save them.
+ const [ localTitle, setLocalTitle ] = useState( title );
+ const [ localBlocks, setLocalBlocks ] = useState( blocks ?? [] );
- setBlocks( blocks ) {
- this.setState( { blocks } );
- }
+ useEffect( () => {
+ if ( ! reusableBlock ) {
+ fetchReusableBlock();
+ }
+ }, [] );
+
+ // If the reusable block was changed by saving another instance of the same
+ // reusable block in the editor, and if this instance is not currently being
+ // edited, update the local state of this instance to match.
+ useEffect( () => {
+ if ( ! isEditing && ! isSaving ) {
+ setLocalTitle( title );
+ }
+ }, [ title, isEditing, isSaving ] );
- setTitle( title ) {
- this.setState( { title } );
- }
+ useEffect( () => {
+ if ( ! isEditing && ! isSaving ) {
+ setLocalBlocks( blocks );
+ }
+ }, [ blocks, isEditing, isSaving ] );
- save() {
- const { onChange, onSave } = this.props;
- const { blocks, title } = this.state;
- const content = serialize( blocks );
- onChange( { title, content } );
+ function save() {
+ onChange( { title: localTitle, content: serialize( localBlocks ) } );
onSave();
-
- this.stopEditing();
+ setIsEditing( false );
}
- render() {
- const {
- isSelected,
- reusableBlock,
- isFetching,
- isSaving,
- canUpdateBlock,
- settings,
- } = this.props;
- const { isEditing, title, blocks } = this.state;
-
- if ( ! reusableBlock && isFetching ) {
+ if ( ! reusableBlock ) {
+ if ( isFetching ) {
return (