Skip to content

Commit 058cc37

Browse files
jsnajdryouknowriadtyxlaellatrixMamaduka
authored
SlotFill: replace valtio with custom ObservableMap (#60879)
* SlotFill: replace valtio with custom ObservableMap * Fill PR number in changelog * Use Set for listeners Co-authored-by: jsnajdr <[email protected]> Co-authored-by: youknowriad <[email protected]> Co-authored-by: tyxla <[email protected]> Co-authored-by: ellatrix <[email protected]> Co-authored-by: Mamaduka <[email protected]> Co-authored-by: jeryj <[email protected]>
1 parent 4b08cf1 commit 058cc37

File tree

9 files changed

+106
-111
lines changed

9 files changed

+106
-111
lines changed

package-lock.json

+2-63
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/components/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
- `CheckboxControl`: Streamline size styles ([#60475](https://github.com/WordPress/gutenberg/pull/60475)).
3030
- Deprecate `reduceMotion` util ([#60839](https://github.com/WordPress/gutenberg/pull/60839)).
3131
- `InputBase`: Simplify management of focus styles. Affects all components based on `InputControl` (e.g. `SearchControl`, `NumberControl`, `UnitControl`), as well as `SelectControl`, `CustomSelectControl`, and `TreeSelect` ([#60226](https://github.com/WordPress/gutenberg/pull/60226)).
32+
- Removed dependency on `valtio`, replaced its usage in `SlotFill` with a custom object [#60879](https://github.com/WordPress/gutenberg/pull/60879)).
3233

3334
## 27.3.0 (2024-04-03)
3435

packages/components/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@
7676
"react-colorful": "^5.3.1",
7777
"remove-accents": "^0.5.0",
7878
"use-lilius": "^2.0.5",
79-
"uuid": "^9.0.1",
80-
"valtio": "1.7.0"
79+
"uuid": "^9.0.1"
8180
},
8281
"peerDependencies": {
8382
"react": "^18.0.0",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { useMemo, useSyncExternalStore } from '@wordpress/element';
5+
6+
export type ObservableMap< K, V > = {
7+
get( name: K ): V | undefined;
8+
set( name: K, value: V ): void;
9+
delete( name: K ): void;
10+
subscribe( name: K, listener: () => void ): () => void;
11+
};
12+
13+
export function observableMap< K, V >(): ObservableMap< K, V > {
14+
const map = new Map< K, V >();
15+
const listeners = new Map< K, Set< () => void > >();
16+
17+
function callListeners( name: K ) {
18+
const list = listeners.get( name );
19+
if ( ! list ) {
20+
return;
21+
}
22+
for ( const listener of list ) {
23+
listener();
24+
}
25+
}
26+
27+
function unsubscribe( name: K, listener: () => void ) {
28+
return () => {
29+
const list = listeners.get( name );
30+
if ( ! list ) {
31+
return;
32+
}
33+
34+
list.delete( listener );
35+
if ( list.size === 0 ) {
36+
listeners.delete( name );
37+
}
38+
};
39+
}
40+
41+
return {
42+
get( name ) {
43+
return map.get( name );
44+
},
45+
set( name, value ) {
46+
map.set( name, value );
47+
callListeners( name );
48+
},
49+
delete( name ) {
50+
map.delete( name );
51+
callListeners( name );
52+
},
53+
subscribe( name, listener ) {
54+
let list = listeners.get( name );
55+
if ( ! list ) {
56+
list = new Set();
57+
listeners.set( name, list );
58+
}
59+
list.add( listener );
60+
61+
return unsubscribe( name, listener );
62+
},
63+
};
64+
}
65+
66+
export function useObservableValue< K, V >(
67+
map: ObservableMap< K, V >,
68+
name: K
69+
): V | undefined {
70+
const [ subscribe, getValue ] = useMemo(
71+
() => [
72+
( listener: () => void ) => map.subscribe( name, listener ),
73+
() => map.get( name ),
74+
],
75+
[ map, name ]
76+
);
77+
return useSyncExternalStore( subscribe, getValue );
78+
}

packages/components/src/slot-fill/bubbles-virtually/slot-fill-context.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import { proxyMap } from 'valtio/utils';
51
/**
62
* WordPress dependencies
73
*/
@@ -11,10 +7,11 @@ import warning from '@wordpress/warning';
117
* Internal dependencies
128
*/
139
import type { SlotFillBubblesVirtuallyContext } from '../types';
10+
import { observableMap } from './observable-map';
1411

1512
const initialContextValue: SlotFillBubblesVirtuallyContext = {
16-
slots: proxyMap(),
17-
fills: proxyMap(),
13+
slots: observableMap(),
14+
fills: observableMap(),
1815
registerSlot: () => {
1916
warning(
2017
'Components must be wrapped within `SlotFillProvider`. ' +

packages/components/src/slot-fill/bubbles-virtually/slot-fill-provider.tsx

+10-18
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import { ref as valRef } from 'valtio';
5-
import { proxyMap } from 'valtio/utils';
6-
71
/**
82
* WordPress dependencies
93
*/
@@ -18,10 +12,11 @@ import type {
1812
SlotFillProviderProps,
1913
SlotFillBubblesVirtuallyContext,
2014
} from '../types';
15+
import { observableMap } from './observable-map';
2116

2217
function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
23-
const slots: SlotFillBubblesVirtuallyContext[ 'slots' ] = proxyMap();
24-
const fills: SlotFillBubblesVirtuallyContext[ 'fills' ] = proxyMap();
18+
const slots: SlotFillBubblesVirtuallyContext[ 'slots' ] = observableMap();
19+
const fills: SlotFillBubblesVirtuallyContext[ 'fills' ] = observableMap();
2520

2621
const registerSlot: SlotFillBubblesVirtuallyContext[ 'registerSlot' ] = (
2722
name,
@@ -30,14 +25,11 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
3025
) => {
3126
const slot = slots.get( name );
3227

33-
slots.set(
34-
name,
35-
valRef( {
36-
...slot,
37-
ref: ref || slot?.ref,
38-
fillProps: fillProps || slot?.fillProps || {},
39-
} )
40-
);
28+
slots.set( name, {
29+
...slot,
30+
ref: ref || slot?.ref,
31+
fillProps: fillProps || slot?.fillProps || {},
32+
} );
4133
};
4234

4335
const unregisterSlot: SlotFillBubblesVirtuallyContext[ 'unregisterSlot' ] =
@@ -74,7 +66,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
7466
name,
7567
ref
7668
) => {
77-
fills.set( name, valRef( [ ...( fills.get( name ) || [] ), ref ] ) );
69+
fills.set( name, [ ...( fills.get( name ) || [] ), ref ] );
7870
};
7971

8072
const unregisterFill: SlotFillBubblesVirtuallyContext[ 'registerFill' ] = (
@@ -88,7 +80,7 @@ function createSlotRegistry(): SlotFillBubblesVirtuallyContext {
8880

8981
fills.set(
9082
name,
91-
valRef( fillsForName.filter( ( fillRef ) => fillRef !== ref ) )
83+
fillsForName.filter( ( fillRef ) => fillRef !== ref )
9284
);
9385
};
9486

Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import { useSnapshot } from 'valtio';
5-
61
/**
72
* WordPress dependencies
83
*/
@@ -13,12 +8,9 @@ import { useContext } from '@wordpress/element';
138
*/
149
import SlotFillContext from './slot-fill-context';
1510
import type { SlotKey } from '../types';
11+
import { useObservableValue } from './observable-map';
1612

1713
export default function useSlotFills( name: SlotKey ) {
1814
const registry = useContext( SlotFillContext );
19-
const fills = useSnapshot( registry.fills, { sync: true } );
20-
// The important bit here is that this call ensures that the hook
21-
// only causes a re-render if the "fills" of a given slot name
22-
// change, not any fills.
23-
return fills.get( name );
15+
return useObservableValue( registry.fills, name );
2416
}

packages/components/src/slot-fill/bubbles-virtually/use-slot.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
/**
2-
* External dependencies
3-
*/
4-
import { useSnapshot } from 'valtio';
5-
61
/**
72
* WordPress dependencies
83
*/
@@ -18,14 +13,11 @@ import type {
1813
FillProps,
1914
SlotKey,
2015
} from '../types';
16+
import { useObservableValue } from './observable-map';
2117

2218
export default function useSlot( name: SlotKey ) {
2319
const registry = useContext( SlotFillContext );
24-
const slots = useSnapshot( registry.slots, { sync: true } );
25-
// The important bit here is that the `useSnapshot` call ensures that the
26-
// hook only causes a re-render if the slot with the given name changes,
27-
// not any other slot.
28-
const slot = slots.get( name );
20+
const slot = useObservableValue( registry.slots, name );
2921

3022
const api = useMemo(
3123
() => ( {

packages/components/src/slot-fill/types.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
*/
44
import type { Component, MutableRefObject, ReactNode, RefObject } from 'react';
55

6+
/**
7+
* Internal dependencies
8+
*/
9+
import type { ObservableMap } from './bubbles-virtually/observable-map';
10+
611
export type DistributiveOmit< T, K extends keyof any > = T extends any
712
? Omit< T, K >
813
: never;
@@ -109,14 +114,14 @@ export type SlotFillBubblesVirtuallyFillRef = MutableRefObject< {
109114
} >;
110115

111116
export type SlotFillBubblesVirtuallyContext = {
112-
slots: Map<
117+
slots: ObservableMap<
113118
SlotKey,
114119
{
115120
ref: SlotFillBubblesVirtuallySlotRef;
116121
fillProps: FillProps;
117122
}
118123
>;
119-
fills: Map< SlotKey, SlotFillBubblesVirtuallyFillRef[] >;
124+
fills: ObservableMap< SlotKey, SlotFillBubblesVirtuallyFillRef[] >;
120125
registerSlot: (
121126
name: SlotKey,
122127
ref: SlotFillBubblesVirtuallySlotRef,

0 commit comments

Comments
 (0)