import { createRef, Component } from 'react'; import SVGDefsContext, { SVGDefsContextProps } from './SVGDefsContext'; import { Defs } from './Defs'; export interface DefsMap { [id: string]: { count: number; node: React.ReactNode; }; } export interface DefsState { defs?: DefsMap; } export interface SVGDefsProviderProps { children?: React.ReactNode; } /** * Renders a `` element and sets up a {@link SVGDefsContext} provider such that child components * may contribute to the `` without the parent component needing explicit knowledge of each contribution. * This helps decouple the parent implementation from the children and ensures that duplicate defs entries, * such as filters, are eliminated. */ class SVGDefsProvider extends Component { private readonly defsRef = createRef(); private readonly defs: DefsMap = {}; private contextValue: SVGDefsContextProps = { addDef: (id, node) => { const defObj = this.defs[id]; if (defObj) { defObj.count++; } else { this.defs[id] = { count: 1, node }; this.updateDefs(); } }, removeDef: (id) => { const defObj = this.defs[id]; if (--defObj.count === 0) { delete this.defs[id]; this.updateDefs(); } } }; private updateDefs() { // Set the defs directly on the child component so that only it will re-render. // Does not use `setState` because otherwise all child components would be re-renders again // when only the `Defs` component needs to be rendered. this.defsRef.current && this.defsRef.current.setDefs(this.defs); } render() { return ( {this.props.children} ); } } export default SVGDefsProvider;