Skip to content

Commit

Permalink
Refactor fill component to avoid relying on componentWillUpdate and u…
Browse files Browse the repository at this point in the history
…se React Hooks instead (#15541)
  • Loading branch information
youknowriad committed May 21, 2019
1 parent 7320d0d commit c38327d
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 48 deletions.
3 changes: 1 addition & 2 deletions packages/components/src/slot-fill/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
} );
}

Expand Down
82 changes: 37 additions & 45 deletions packages/components/src/slot-fill/fill.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 ) => (
Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/slot-fill/slot.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) ) {
Expand Down

0 comments on commit c38327d

Please sign in to comment.