Portal을 하위 트리로 반응
19170 단어 reactreactportal
문제
아래는 제가 최근에 접한 사용 사례입니다. 아래와 같은 표준 사이드바 셸 UI 디자인을 상상해 보십시오.
사이드바 + 이동 경로는 애플리케이션의 "쉘"이며 콘텐츠는 개별 페이지에서 렌더링됩니다.
React 구성 요소 구조는 다음과 같습니다.
<AppShell>
<RouteOutlet />
</AppShell>
여기서
<RouteOutlet />
는 URL을 기반으로 자식을 렌더링하는 React Router Switch/Route 구성입니다.언뜻 보기에는 간단해 보일 수 있지만 까다로운 부분은 이동 경로가 렌더링되는 페이지에 따라 동적이어야 한다는 것입니다.
이를 수행하는 한 가지 방법은 React Context 을 사용하는 것입니다.
const BreadCrumbContext = React.createContext(function noop() {});
const AppShell = ({ children }) => {
const [breadcrumbs, setBreadcrumbs] = React.useState([]);
return (
<BreadCrumbContext.Provider value={setBreadcrumbs}>
<Sidebar />
<div>
<Breadcrumbs values={breadcrumbs} />
{children}
</div>
</BreadCrumbContext.Provider>
);
};
// custom hook to be used by page that want to add breadcrumbs
const useBreadcrumbs = (breadcrumbValues) => {
const setBreadcrumbs = React.useContext(BreadCrumbContext);
React.useEffect(() => {
setBreadcrumbs(breadcrumbValues);
return () => setBreadcrumbs([]);
}, [breadcrumbValues, setBreadcrumbs]);
};
const MyPage = ({ customer }) => {
useBreadcrumbs(['Customer', customer.name]);
return <div>...Other Content</div>;
};
솔루션은 작동하지만 이러한 모든 공급자 및 사용자 지정 후크를 설정하는 것은 상당히 지루합니다.
더 간단할 수 있습니까?
해결책
React에는 상대적으로 덜 사용되는 기능Portal이 있어 자식을 DOM 계층 외부에 있는 DOM 노드로 렌더링할 수 있습니다.
그러나 공식 문서(및 온라인에서 찾을 수 있는 대부분의 문서)에서 포털의 사용 사례는 대화/도구 설명 등과 같은 사용 사례에 유용한
document.body
의 루트에 자녀를 추가하는 것입니다.그러나 대상이
document.body
가 아니라 다른 React 하위 트리라면 어떻게 될까요?그러면 위의 문제가 해결되어 페이지에서 탐색 경로로 렌더링됩니다!
해결책은 다음과 같습니다.
// custom hook to force React to rerender, hook version of `forceUpdate` of class component
function useForceUpdate() {
const [, dispatch] = React.useState(Object.create(null));
return React.useCallback(() => {
dispatch(Object.create(null));
}, []);
}
// simple event emitter. Read https://malcolmkee.com/blog/simple-event-bus/ for a more detailed explanation.
function createEventBus() {
const listeners = [];
return {
listen: (listener) => {
listeners.push(listener);
return () => {
listeners.splice(listeners.indexOf(listener), 1);
};
},
emit: () => listeners.forEach((l) => l()),
};
}
// this is where the magic is
function createFillSlot() {
// create a ref to get a reference of the target that we want to render into
const ref = React.createRef();
// setup the event emitter
const eventBus = createEventBus();
// Slot is where we want to render. It is just an empty div.
function Slot() {
React.useEffect(() => {
if (ref.current) {
// ask the event emitter to tell the whole world the slot is ready to be used
eventBus.emit();
}
}, []);
return <div ref={ref} />;
}
// Fill is where we render the content we want to inject to the Slot
function Fill({ children }) {
const forceUpdate = useForceUpdate();
// when Slot is rendered, we will get notified by event bus, re-render
React.useEffect(() => eventBus.listen(forceUpdate), [forceUpdate]);
return ref.current ? ReactDOM.createPortal(children, ref.current) : null;
}
return {
Slot,
Fill,
};
}
const Breadcrumb = createFillSlot();
// This is where we want to show the content
const Header = () => {
return (
<div className="p-2 flex items-center bg-white text-black shadow-lg">
Header <Breadcrumb.Slot />
</div>
);
};
const Page1 = () => {
return (
<div>
<h2>Page 1</h2>
<Breadcrumb.Fill>
Hello > <a href="#">Page 1</a>
</Breadcrumb.Fill>
</div>
);
};
const Page2 = () => {
return (
<div>
<h2>Page 2</h2>
<Breadcrumb.Fill>
Hello > <a href="#">Page 2</a>
</Breadcrumb.Fill>
</div>
);
};
const App = () => {
const [page, setPage] = React.useState('');
return (
<div className="flex">
<div className="flex flex-col space-y-2 px-3 items-start">
<button onClick={() => setPage('1')}>Show Page 1</button>
<button onClick={() => setPage('2')}>Show Page 2</button>
</div>
<div className="flex-1">
<Header />
<div className="p-3 bg-gray-100 text-gray-600">
{page === '1' && <Page1 />}
{page === '2' && <Page2 />}
</div>
</div>
</div>
);
};
render(<App />);
Reference
이 문제에 관하여(Portal을 하위 트리로 반응), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/malcolmkee/react-portal-to-subtree-50ae텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)