From c38327df97fc250d9af5d340f2f68a5db7c76eaf Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 21 May 2019 09:08:15 +0100 Subject: [PATCH] Refactor fill component to avoid relying on componentWillUpdate and use React Hooks instead (#15541) --- packages/components/src/slot-fill/context.js | 3 +- packages/components/src/slot-fill/fill.js | 82 +++++++++----------- packages/components/src/slot-fill/slot.js | 2 +- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/packages/components/src/slot-fill/context.js b/packages/components/src/slot-fill/context.js index a39ac94013e0af..7ee0d9806c4003 100644 --- a/packages/components/src/slot-fill/context.js +++ b/packages/components/src/slot-fill/context.js @@ -97,13 +97,12 @@ class SlotFillProvider extends Component { if ( this.slots[ name ] !== slotInstance ) { return []; } - return sortBy( this.fills[ name ], 'occurrence' ); } resetFillOccurrence( name ) { forEach( this.fills[ name ], ( instance ) => { - instance.resetOccurrence(); + instance.occurrence = undefined; } ); } diff --git a/packages/components/src/slot-fill/fill.js b/packages/components/src/slot-fill/fill.js index 7c2b3fb0ad5a28..a0c2876643f048 100644 --- a/packages/components/src/slot-fill/fill.js +++ b/packages/components/src/slot-fill/fill.js @@ -6,7 +6,7 @@ import { isFunction } from 'lodash'; /** * WordPress dependencies */ -import { Component, createPortal } from '@wordpress/element'; +import { createPortal, useLayoutEffect, useRef, useState } from '@wordpress/element'; /** * Internal dependencies @@ -15,64 +15,56 @@ import { Consumer } from './context'; let occurrences = 0; -class FillComponent extends Component { - constructor() { - super( ...arguments ); - this.occurrence = ++occurrences; - } +function FillComponent( { name, getSlot, children, registerFill, unregisterFill } ) { + // Random state used to rerender the component if needed, ideally we don't need this + const [ , updateRerenderState ] = useState( {} ); + const rerender = () => updateRerenderState( {} ); - componentDidMount() { - const { registerFill } = this.props; + const ref = useRef( { + name, + children, + } ); - registerFill( this.props.name, this ); + if ( ! ref.current.occurrence ) { + ref.current.occurrence = ++occurrences; } - componentWillUpdate() { - if ( ! this.occurrence ) { - this.occurrence = ++occurrences; - } - const { getSlot } = this.props; - const slot = getSlot( this.props.name ); + useLayoutEffect( () => { + ref.current.forceUpdate = rerender; + registerFill( name, ref.current ); + return () => unregisterFill( name, ref.current ); + }, [] ); + + useLayoutEffect( () => { + ref.current.children = children; + const slot = getSlot( name ); if ( slot && ! slot.props.bubblesVirtually ) { slot.forceUpdate(); } - } - - componentWillUnmount() { - const { unregisterFill } = this.props; + }, [ children ] ); - unregisterFill( this.props.name, this ); - } + useLayoutEffect( () => { + if ( name === ref.current.name ) { + // ignore initial effect + return; + } + unregisterFill( ref.current.name, ref.current ); + ref.current.name = name; + registerFill( name, ref.current ); + }, [ name ] ); - componentDidUpdate( prevProps ) { - const { name, unregisterFill, registerFill } = this.props; + const slot = getSlot( name ); - if ( prevProps.name !== name ) { - unregisterFill( prevProps.name, this ); - registerFill( name, this ); - } + if ( ! slot || ! slot.node || ! slot.props.bubblesVirtually ) { + return null; } - resetOccurrence() { - this.occurrence = null; + // If a function is passed as a child, provide it with the fillProps. + if ( isFunction( children ) ) { + children = children( slot.props.fillProps ); } - render() { - const { name, getSlot } = this.props; - let { children } = this.props; - const slot = getSlot( name ); - - if ( ! slot || ! slot.node || ! slot.props.bubblesVirtually ) { - return null; - } - - // If a function is passed as a child, provide it with the fillProps. - if ( isFunction( children ) ) { - children = children( slot.props.fillProps ); - } - - return createPortal( children, slot.node ); - } + return createPortal( children, slot.node ); } const Fill = ( props ) => ( diff --git a/packages/components/src/slot-fill/slot.js b/packages/components/src/slot-fill/slot.js index 68f11b1395b83a..6de69f74f865a3 100644 --- a/packages/components/src/slot-fill/slot.js +++ b/packages/components/src/slot-fill/slot.js @@ -64,7 +64,7 @@ class SlotComponent extends Component { const fills = map( getFills( name, this ), ( fill ) => { const fillKey = fill.occurrence; - const fillChildren = isFunction( fill.props.children ) ? fill.props.children( fillProps ) : fill.props.children; + const fillChildren = isFunction( fill.children ) ? fill.children( fillProps ) : fill.children; return Children.map( fillChildren, ( child, childIndex ) => { if ( ! child || isString( child ) ) {