Skip to content

Instantly share code, notes, and snippets.

@developit
Created August 1, 2023 01:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save developit/1a3762e8b18a3248df604a7657cef95b to your computer and use it in GitHub Desktop.
Save developit/1a3762e8b18a3248df604a7657cef95b to your computer and use it in GitHub Desktop.
import { createContext } from 'preact';
import { useContext, useRef, useMemo, useState, useLayoutEffect } from 'preact/hooks';
function createSlotContext() {
const slots = {};
const owners = {};
const subs = [];
const sub = (name, fn) => {
const e = [name, fn];
subs.push(e);
return () => {
subs.splice(subs.indexOf(e) >>> 0, 1);
};
};
const pub = (name, value, owner) => {
slots[name] = value;
owners[name] = owner;
subs.forEach(s => s[0] === name && s[1](value));
};
return { slots, owners, sub, pub };
}
const globalContext = createSlotContext();
const SlotContext = createContext(globalContext);
export function SlotsProvider(props) {
const value = useMemo(createSlotContext, []);
return <SlotContext.Provider value={value} {...props} />;
}
export function Slot({ name }) {
const { slots, sub } = useContext(SlotContext);
const [slotted, update] = useState(slots[name]);
useLayoutEffect(() => sub(name, update), [name]);
return slotted;
}
let c = 0;
export function SlotContent({ name, children = null }) {
const { owners, pub } = useContext(SlotContext);
const content = useRef();
const owner = useRef();
const initial = useRef(true);
if (!owner.current) pub(name, children, (owner.current = ++c));
content.current = children;
useLayoutEffect(() => {
if (!initial.current) pub(name, content.current, owner.current);
initial.current = false;
return () => owners[name] === owner.current && pub(name, null, 0);
}, [name]);
return null;
}
import { createContext, useContext, useRef, useMemo, useState, useLayoutEffect } from 'react';
function createSlotContext() {
const slots = {};
const owners = {};
const subs = [];
const sub = (name, fn) => {
const e = [name, fn];
subs.push(e);
return () => {
subs.splice(subs.indexOf(e) >>> 0, 1);
};
};
const pub = (name, value, owner) => {
slots[name] = value;
owners[name] = owner;
subs.forEach(s => s[0] === name && s[1](value));
};
return { slots, owners, sub, pub };
}
const globalContext = createSlotContext();
const SlotContext = createContext(globalContext);
export function SlotsProvider(props) {
const value = useMemo(createSlotContext, []);
return <SlotContext.Provider value={value} {...props} />;
}
export function Slot({ name }) {
const { slots, sub } = useContext(SlotContext);
const [slotted, update] = useState(slots[name]);
useLayoutEffect(() => sub(name, update), [name]);
return slotted;
}
let c = 0;
export function SlotContent({ name, children = null }) {
const { owners, pub } = useContext(SlotContext);
const content = useRef();
const owner = useRef();
const initial = useRef(true);
if (!owner.current) pub(name, children, (owner.current = ++c));
content.current = children;
useLayoutEffect(() => {
if (!initial.current) pub(name, content.current, owner.current);
initial.current = false;
return () => owners[name] === owner.current && pub(name, null, 0);
}, [name]);
return null;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment