Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SlotFill: remove explicit rerender from portal version #67471

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 7 additions & 11 deletions packages/components/src/slot-fill/bubbles-virtually/fill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import { useObservableValue } from '@wordpress/compose';
import {
useContext,
useReducer,
useRef,
useEffect,
createPortal,
Expand All @@ -20,18 +19,15 @@ import type { FillComponentProps } from '../types';
export default function Fill( { name, children }: FillComponentProps ) {
const registry = useContext( SlotFillContext );
const slot = useObservableValue( registry.slots, name );
const [ , rerender ] = useReducer( () => [], [] );
const ref = useRef( { rerender } );
const instanceRef = useRef( {} );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rerender API goes away and the fill now registers only an unique instance object. I'm renaming it from ref to instance when registering, something that @tyxla asked for in one of the previous PRs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!


// We register fills so we can keep track of their existence.
// Slots can use the `useSlotFills` hook to know if there're already fills
// registered so they can choose to render themselves or not.
useEffect( () => {
// We register fills so we can keep track of their existence.
// Some Slot implementations need to know if there're already fills
// registered so they can choose to render themselves or not.
const refValue = ref.current;
registry.registerFill( name, refValue );
return () => {
registry.unregisterFill( name, refValue );
};
const instance = instanceRef.current;
registry.registerFill( name, instance );
return () => registry.unregisterFill( name, instance );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This effect stays the same except renaming the ref variable to instanceRef etc.

}, [ registry, name ] );

if ( ! slot || ! slot.ref.current ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
ref,
fillProps
) => {
const slot = slots.get( name );

slots.set( name, {
...slot,
ref: ref || slot?.ref,
fillProps: fillProps || slot?.fillProps || {},
} );
slots.set( name, { ref, fillProps } );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A drive-by change removing redundant code. All the registerSlot calls always include both the ref and fillProps values. Inheriting them from the previous slot value has no effect.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense 👍

};

const unregisterSlot: SlotFillBubblesVirtuallyContext[ 'unregisterSlot' ] =
Expand Down Expand Up @@ -66,12 +60,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
return;
}

slot.fillProps = fillProps;
const slotFills = fills.get( name );
if ( slotFills ) {
// Force update fills.
slotFills.forEach( ( fill ) => fill.rerender() );
}
slots.set( name, { ref, fillProps } );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the key change in this PR! Instead of silently updating a field with slots.get( name ).fillProps = fillProps, we call slots.set. That sets a new value and triggers calling all observableMap listeners, leading to automatic rerendering of all fills.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much clearer and more concise, looks great 🚀

};

const registerFill: SlotFillBubblesVirtuallyContext[ 'registerFill' ] = (
Expand Down
11 changes: 4 additions & 7 deletions packages/components/src/slot-fill/bubbles-virtually/slot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ function Slot(
...restProps
} = props;

const { registerSlot, unregisterSlot, ...registry } =
useContext( SlotFillContext );
const registry = useContext( SlotFillContext );

const ref = useRef< HTMLElement >( null );

Expand All @@ -54,11 +53,9 @@ function Slot(
}, [ fillProps ] );

useLayoutEffect( () => {
registerSlot( name, ref, fillPropsRef.current );
return () => {
unregisterSlot( name, ref );
};
}, [ registerSlot, unregisterSlot, name ] );
registry.registerSlot( name, ref, fillPropsRef.current );
return () => registry.unregisterSlot( name, ref );
}, [ registry, name ] );
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A mere style change: don't destructure fields of registry. Simplifies the hook dependencies.


useLayoutEffect( () => {
registry.updateSlot( name, ref, fillPropsRef.current );
Expand Down
7 changes: 4 additions & 3 deletions packages/components/src/slot-fill/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,16 @@ export type SlotFillProviderProps = {

export type SlotRef = RefObject< HTMLElement >;
export type Rerenderable = { rerender: () => void };
export type FillInstance = {};

export type SlotFillBubblesVirtuallyContext = {
slots: ObservableMap< SlotKey, { ref: SlotRef; fillProps: FillProps } >;
fills: ObservableMap< SlotKey, Rerenderable[] >;
fills: ObservableMap< SlotKey, FillInstance[] >;
registerSlot: ( name: SlotKey, ref: SlotRef, fillProps: FillProps ) => void;
unregisterSlot: ( name: SlotKey, ref: SlotRef ) => void;
updateSlot: ( name: SlotKey, ref: SlotRef, fillProps: FillProps ) => void;
registerFill: ( name: SlotKey, ref: Rerenderable ) => void;
unregisterFill: ( name: SlotKey, ref: Rerenderable ) => void;
registerFill: ( name: SlotKey, instance: FillInstance ) => void;
unregisterFill: ( name: SlotKey, instance: FillInstance ) => void;

/**
* This helps the provider know if it's using the default context value or not.
Expand Down
Loading