Skip to content

Instantly share code, notes, and snippets.

@sethdavis512
Last active December 9, 2022 03:28
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 sethdavis512/2bf4c3542f7a816868dd77323be61f58 to your computer and use it in GitHub Desktop.
Save sethdavis512/2bf4c3542f7a816868dd77323be61f58 to your computer and use it in GitHub Desktop.
A component that only shows one of its children and allows dynamic switching between children
import React, {
useContext,
createContext,
ReactNode,
useMemo,
Children,
isValidElement,
useState
} from 'react';
interface SwitchboardContextInterface {
switchTo: (r: string) => void;
}
interface SwitchboardProps {
children: ReactNode;
initialId?: string;
}
type MapOfChildrenType = Map<string, ReactNode>;
export const SwitchboardContext = createContext<SwitchboardContextInterface | undefined>(undefined);
export const useSwitchboardContext = (): SwitchboardContextInterface => {
const context = useContext(SwitchboardContext);
if (context === undefined) {
throw new Error('❌ useSwitchboardContext must be used within a Switchboard');
}
return { switchTo: context.switchTo };
};
export default function Switchboard({ children, initialId }: SwitchboardProps): JSX.Element {
const mapOfChildren = useMemo(() => {
const initialMap: MapOfChildrenType = new Map();
return Children.toArray(children).reduce<MapOfChildrenType>((childrenMap, currentChild) => {
if (typeof currentChild === 'string' || typeof currentChild === 'number') {
console.error('Switchboard child is invalid');
} else if (isValidElement(currentChild) && !childrenMap.has(currentChild.props.id)) {
childrenMap.set(currentChild.props.id, currentChild);
}
return childrenMap;
}, initialMap);
}, [children]);
const [firstId] = mapOfChildren.keys();
const [currentId, setCurrentId] = useState(initialId || firstId);
const currentComponent = mapOfChildren.get(currentId);
const switchTo = (to: string): void => {
if (!mapOfChildren.has(to)) {
console.error(`The "to" value "${to}" does not exist in the generated map.`);
}
setCurrentId(to);
};
return (
<SwitchboardContext.Provider value={{ switchTo }}>
{currentComponent}
</SwitchboardContext.Provider>
);
}
// ================
interface SwitchboardButtonProps {
to: string;
}
function SwitchboardButton(props: SwitchboardButtonProps): JSX.Element {
const { switchTo } = useSwitchboardContext();
const handleClick = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
if (props.onClick) {
props.onClick(event);
}
switchTo(props.to);
};
return (
<button onClick={handleClick} type="button" {...props}>
{props.children}
</button>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment