-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
taro 能支持 react-dom 中的 createPortal 吗,或实现类似api #7282
Comments
+1。参考 remaxjs/remax#1046 |
+1。 |
+1 |
请问有计划支持吗 @Chen-jj |
+1 |
2 similar comments
+1 |
+1 |
所以现在是怎样,实现了吗,有计划吗 |
目前是自己实现了一个,原理就是每个页面包一个container,然后通过useReducer、createContext将portal组件包裹的内容渲染到container里 |
我参考 ant-design-mobile 的 https://github.com/ant-design/ant-design-mobile-rn/tree/4344e2850727a3fa1c1f7691f362438e2a3a6bfc/components/portal 实现了一个 Portal 组件。 Portal.tsx import { PropsWithChildren, useContext, useEffect, useState } from "react";
import PortalProvider, { PortalContext } from "./PortalProvider";
import PortalSlot from "./PortalSlot";
const Portal = ({ children }: PropsWithChildren<{}>) => {
const manage = useContext(PortalContext);
const [key, setKey] = useState(0);
// 获取 key
useEffect(() => {
if (manage?.getKey) {
if (!key) {
setKey(manage.getKey());
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [manage?.getKey]);
// 挂载内容
useEffect(() => {
if (key) {
manage?.mount(key, children);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [children, key]);
// 挂载内容
useEffect(() => {
return () => {
if (key) {
manage?.umount(key);
}
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [key]);
return <></>;
};
Portal.Provider = PortalProvider;
Portal.Slot = PortalSlot;
export default Portal; PortalSlot.tsx import { PortalContext } from "./PortalProvider";
const PortalSlot = () => {
return (
<PortalContext.Consumer>
{manage => {
return (
<>
{/* 展示挂载过来的 portal */}
{Object.keys(manage?.portalChildren || {}).map(key => {
return manage?.portalChildren[key];
})}
</>
);
}}
</PortalContext.Consumer>
);
};
export default PortalSlot; PortalProvider.tsx import React, {
PropsWithChildren,
useCallback,
useMemo,
useRef,
useState
} from "react";
interface PortalChildren {
[key: number]: React.ReactNode;
}
interface Manage {
getKey: () => number;
mount: (key: number, children: React.ReactNode) => void;
umount: (key: number) => void;
portalChildren: PortalChildren;
}
export const PortalContext = React.createContext<Manage | undefined>(undefined);
// 提供将 children 挂载到指定位置的能力,配合 PortalSlot 实现
const PortalProvider = ({ children }: PropsWithChildren<{}>) => {
const nextKey = useRef(0);
const [portalChildren, setPortalChildren] = useState<PortalChildren>({});
const mount = useCallback((key: number, c: React.ReactNode) => {
setPortalChildren(currentPortalChildren => {
currentPortalChildren[key] = c;
return { ...currentPortalChildren };
});
}, []);
const umount = useCallback((key: number) => {
setPortalChildren(currentPortalChildren => {
delete currentPortalChildren[key];
return { ...currentPortalChildren };
});
}, []);
const getKey = useCallback(() => {
nextKey.current++;
return nextKey.current;
}, []);
const manage: Manage = useMemo(
() => ({
getKey,
mount,
umount,
portalChildren
}),
[getKey, mount, portalChildren, umount]
);
return (
<PortalContext.Provider value={manage}>{children}</PortalContext.Provider>
);
};
export default PortalProvider; 在页面的外层添加 PortalProvider,在指定挂载的位置放置 PortalSlot,然后就可以在 Modal 之类的组件里面使用 Portal 组件包装,实现在指定的 dom 位置渲染。 <Portal.Provider>
{children}
<Portal.Slot></Portal.Slot>
</Portal.Provider> <Portal>
<Modal>...</Modal>
</Portal> |
有个疑惑,Portal.Provider挂到app.js中的render可以的吗 |
Taro 已具备 import { useState, useEffect } from 'react'
import { createPortal } from "@tarojs/react";
export default function Index() {
const [dom, setDom] = useState(null);
useEffect(() => {
const dom = document.getElementById("my-portal");
setDom(dom);
setTimeout(() => {
setDom(null);
}, 3000);
}, []);
return (
<View className="index">
<Text>Hello world!</Text>
<View id="my-portal"></View>
{dom && createPortal(<View>你好世界</View>, dom)}
</View>
);
} 其中, 同时,微信小程序也提供了 import { useState } from 'react'
import { RootPortal, View, Button } from '@tarojs/components'
export default function RootPortalExample {
const [show, setShow] = useState(false)
const toggle = () => {
setShow(!show)
}
render () {
return (
<View>
<Button onClick={toggle}>显示root-portal</Button>
{
show && (<RootPortal><View>content</View></RootPortal>)
}
</View>
)
}
} |
依赖createPortal实现root-portal import { useRouter } from '@tarojs/taro'
import { createPortal } from "@tarojs/react";
import { useLayoutEffect, useState } from "react";
const RootPortal = ({ children, enable = true }) => {
const router = useRouter()
const [dom, setDom] = useState()
useLayoutEffect(() => {
const _dom = document.getElementById(router.$taroPath);
_dom && setDom(_dom)
}, [router.$taroPath])
return (enable && dom) ? createPortal(children, _dom) : children
}
export default RootPortal |
请问这种方式小程序支持吗 |
只要是支持Taro的小程序都支持, 他操作的是react的虚拟dom |
啥也不说了,大哥牛逼 |
这个特性解决了什么问题?
一些组件更适用于适用createPortal在其他节点生成
这个 API ### 长什么样?
createPortal(<></>, otherNode)
The text was updated successfully, but these errors were encountered: