xref: /MusicFree/src/components/base/portal.tsx (revision e650bfb34226e2a09d15cbf7832c4805a87cd60e)
1import React, {ReactNode, useEffect, useRef} from 'react';
2import {StyleSheet, View} from 'react-native';
3import {atom, useAtomValue, useSetAtom} from 'jotai';
4
5interface IPortalNode {
6    key: string | null;
7    children: ReactNode;
8}
9
10const portalsAtom = atom<IPortalNode[]>([]);
11
12interface IPortalProps {
13    children: ReactNode;
14}
15export default function Portal(props: IPortalProps) {
16    const {children} = props;
17
18    const keyRef = useRef<string | null>(null);
19    const setPortalsAtoms = useSetAtom(portalsAtom);
20
21    useEffect(() => {
22        if (!keyRef.current) {
23            // mount
24            keyRef.current = Math.random().toString().slice(2);
25            // console.log("MOUNT!", keyRef.current);
26            setPortalsAtoms(portals => [
27                ...portals,
28                {key: keyRef.current, children},
29            ]);
30        } else {
31            // update
32            // console.log("UPDATE!", keyRef.current);
33            setPortalsAtoms(portals =>
34                portals.map(it =>
35                    it.key === keyRef.current ? {...it, children} : it,
36                ),
37            );
38        }
39    }, [children]);
40
41    useEffect(() => {
42        return () => {
43            if (keyRef.current) {
44                // console.log("UNMOUNT!", keyRef.current);
45                setPortalsAtoms(portals =>
46                    portals.filter(it => it.key !== keyRef.current),
47                );
48            }
49        };
50    }, []);
51
52    return null;
53}
54
55export function PortalHost() {
56    const portals = useAtomValue(portalsAtom);
57
58    return (
59        <>
60            {portals.map(({key, children}) => (
61                <View
62                    key={key}
63                    collapsable={false}
64                    pointerEvents="box-none"
65                    style={StyleSheet.absoluteFill}>
66                    {children}
67                </View>
68            ))}
69        </>
70    );
71}
72