Skip to content

Instantly share code, notes, and snippets.

@AimWhy
Created November 26, 2022 09:33
Show Gist options
  • Save AimWhy/889b65fdde2cf8f678af7d64e22475ff to your computer and use it in GitHub Desktop.
Save AimWhy/889b65fdde2cf8f678af7d64e22475ff to your computer and use it in GitHub Desktop.
import React from 'react';
import useForceUpdate from './use-force-update';
import useLayoutEffect from './useIsomorphicLayoutEffect';
export const createSlots = (slotNames) => {
const SlotsContext = React.createContext({
registerSlot: () => null,
unregisterSlot: () => null,
context: {},
});
const defaultContext = Object.freeze({});
const Slots = ({ context = defaultContext, children }) => {
const slotsDefinition = {};
slotNames.map((name) => (slotsDefinition[name] = null));
const slotsRef = React.useRef(slotsDefinition);
const rerenderWithSlots = useForceUpdate();
const [isMounted, setIsMounted] = React.useState(false);
useLayoutEffect(() => {
rerenderWithSlots();
setIsMounted(true);
}, [rerenderWithSlots]);
const registerSlot = React.useCallback(
(name, contents) => {
slotsRef.current[name] = contents;
if (isMounted) {
rerenderWithSlots();
}
},
[isMounted, rerenderWithSlots]
);
const unregisterSlot = React.useCallback(
(name) => {
slotsRef.current[name] = null;
rerenderWithSlots();
},
[rerenderWithSlots]
);
const slots = slotsRef.current;
return (
<SlotsContext.Provider value={{ registerSlot, unregisterSlot, context }}>
{children(slots)}
</SlotsContext.Provider>
);
};
const Slot = ({ name, children }) => {
const { registerSlot, unregisterSlot, context } = React.useContext(SlotsContext);
useLayoutEffect(() => {
registerSlot(name, typeof children === 'function' ? children(context) : children);
return () => unregisterSlot(name);
}, [name, children, registerSlot, unregisterSlot, context]);
return null;
};
return { Slots, Slot };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment