是否可以在 React-Router V6 中的一个组件中使用多个插座
Is it possible to use multiple outlets in a component in React-Router V6
我在应用程序中使用 React Router v6。我有一个布局页面,它使用一个插座来显示主要内容。我还想包含一个标题部分,该部分会根据匹配的路径而变化,但我不确定该怎么做。
function MainContent() {
return (
<div>
<div>{TITLE SHOULD GO HERE}</div>
<div><Outlet /></div>
</div>
);
}
function MainApp() {
return (
<Router>
<Routes>
<Route path="/projects" element={<MainContent />} >
<Route index element={<ProjectList />} title="Projects" />
<Route path="create" element={<CreateProject />} title="Create Project" />
</Route>
<Routes/>
</Router>
);
}
这样的事情可能吗?理想情况下,我希望除了标题之外还有一些其他的道具,我可以通过这种方式控制,所以像这样的变化有一个好的组织系统会很棒。
最直接的方法是将 title
属性移动到 MainContent
布局包装器并单独包装每个路由,但您将丢失嵌套路由。
另一种方法是创建一个 React 上下文来保存 title
状态并使用包装器组件来设置标题。
const TitleContext = createContext({
title: "",
setTitle: () => {}
});
const useTitle = () => useContext(TitleContext);
const TitleProvider = ({ children }) => {
const [title, setTitle] = useState("");
return (
<TitleContext.Provider value={{ title, setTitle }}>
{children}
</TitleContext.Provider>
);
};
使用提供程序包装应用程序(或任何高于 Routes
组件 的祖先组件)。
<TitleProvider>
<App />
</TitleProvider>
更新 MainContent
以访问 useTitle
挂钩以获取当前 title
值并呈现它。
function MainContent() {
const { title } = useTitle();
return (
<div>
<h1>{title}</h1>
<div>
<Outlet />
</div>
</div>
);
}
TitleWrapper
组件。
const TitleWrapper = ({ children, title }) => {
const { setTitle } = useTitle();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
return children;
};
并更新路由组件以包装在 TitleWrapper
组件中,在此处传递 title
属性。
<Route path="/projects" element={<MainContent />}>
<Route
index
element={
<TitleWrapper title="Projects">
<ProjectList />
</TitleWrapper>
}
/>
<Route
path="create"
element={
<TitleWrapper title="Create Project">
<CreateProject />
</TitleWrapper>
}
/>
</Route>
这样,MainContent
可以被认为是UI一组路由共有的,而TitleWrapper
(你可以选择一个更合适的名字) 可以被认为是 UI 特定于一条路线。
更新
我忘记了 Outlet
组件提供自己的 React 上下文。这变得更微不足道了。谢谢@LIIT。
示例:
import { useOutletContext } from 'react-router-dom';
const useTitle = (title) => {
const { setTitle } = useOutletContext();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
};
...
function MainContent() {
const [title, setTitle] = useState("");
return (
<div>
<h1>{title}</h1>
<div>
<Outlet context={{ title, setTitle }} />
</div>
</div>
);
}
...
const CreateProject = ({ title }) => {
useTitle(title);
return ...;
};
...
<Router>
<Routes>
<Route path="/projects" element={<MainContent />}>
<Route index element={<ProjectList title="Projects" />} />
<Route
path="create"
element={<CreateProject title="Create Project" />}
/>
</Route>
</Routes>
</Router>
对于 left-right 布局,我遇到了同样的问题:更改侧边栏内容和主要内容,而不重复样式、横幅等。
我发现的最简单的方法是删除嵌套路由,并创建一个布局组件,在其中我通过属性提供不断变化的内容。
布局组件(为此 post 剥离):
export function Layout(props) {
return (
<>
<div class="left-sidebar">
<img id="logo" src={Logo} alt="My logo" />
{props.left}
</div>
<div className='right'>
<header className="App-header">
<h1>This is big text!</h1>
</header>
<nav>
<NavLink to="/a">A</NavLink>
|
<NavLink to="/b">B</NavLink>
</nav>
<main>
{props.right}
</main>
</div>
</>
);
}
在反应路由器中的用法:
<Route path="myPath" element={
<Layout left={<p>I'm left</p>}
right={<p>I'm right</p>} />
} />
我在应用程序中使用 React Router v6。我有一个布局页面,它使用一个插座来显示主要内容。我还想包含一个标题部分,该部分会根据匹配的路径而变化,但我不确定该怎么做。
function MainContent() {
return (
<div>
<div>{TITLE SHOULD GO HERE}</div>
<div><Outlet /></div>
</div>
);
}
function MainApp() {
return (
<Router>
<Routes>
<Route path="/projects" element={<MainContent />} >
<Route index element={<ProjectList />} title="Projects" />
<Route path="create" element={<CreateProject />} title="Create Project" />
</Route>
<Routes/>
</Router>
);
}
这样的事情可能吗?理想情况下,我希望除了标题之外还有一些其他的道具,我可以通过这种方式控制,所以像这样的变化有一个好的组织系统会很棒。
最直接的方法是将 title
属性移动到 MainContent
布局包装器并单独包装每个路由,但您将丢失嵌套路由。
另一种方法是创建一个 React 上下文来保存 title
状态并使用包装器组件来设置标题。
const TitleContext = createContext({
title: "",
setTitle: () => {}
});
const useTitle = () => useContext(TitleContext);
const TitleProvider = ({ children }) => {
const [title, setTitle] = useState("");
return (
<TitleContext.Provider value={{ title, setTitle }}>
{children}
</TitleContext.Provider>
);
};
使用提供程序包装应用程序(或任何高于 Routes
组件 的祖先组件)。
<TitleProvider>
<App />
</TitleProvider>
更新 MainContent
以访问 useTitle
挂钩以获取当前 title
值并呈现它。
function MainContent() {
const { title } = useTitle();
return (
<div>
<h1>{title}</h1>
<div>
<Outlet />
</div>
</div>
);
}
TitleWrapper
组件。
const TitleWrapper = ({ children, title }) => {
const { setTitle } = useTitle();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
return children;
};
并更新路由组件以包装在 TitleWrapper
组件中,在此处传递 title
属性。
<Route path="/projects" element={<MainContent />}>
<Route
index
element={
<TitleWrapper title="Projects">
<ProjectList />
</TitleWrapper>
}
/>
<Route
path="create"
element={
<TitleWrapper title="Create Project">
<CreateProject />
</TitleWrapper>
}
/>
</Route>
这样,MainContent
可以被认为是UI一组路由共有的,而TitleWrapper
(你可以选择一个更合适的名字) 可以被认为是 UI 特定于一条路线。
更新
我忘记了 Outlet
组件提供自己的 React 上下文。这变得更微不足道了。谢谢@LIIT。
示例:
import { useOutletContext } from 'react-router-dom';
const useTitle = (title) => {
const { setTitle } = useOutletContext();
useEffect(() => {
setTitle(title);
}, [setTitle, title]);
};
...
function MainContent() {
const [title, setTitle] = useState("");
return (
<div>
<h1>{title}</h1>
<div>
<Outlet context={{ title, setTitle }} />
</div>
</div>
);
}
...
const CreateProject = ({ title }) => {
useTitle(title);
return ...;
};
...
<Router>
<Routes>
<Route path="/projects" element={<MainContent />}>
<Route index element={<ProjectList title="Projects" />} />
<Route
path="create"
element={<CreateProject title="Create Project" />}
/>
</Route>
</Routes>
</Router>
对于 left-right 布局,我遇到了同样的问题:更改侧边栏内容和主要内容,而不重复样式、横幅等。
我发现的最简单的方法是删除嵌套路由,并创建一个布局组件,在其中我通过属性提供不断变化的内容。
布局组件(为此 post 剥离):
export function Layout(props) {
return (
<>
<div class="left-sidebar">
<img id="logo" src={Logo} alt="My logo" />
{props.left}
</div>
<div className='right'>
<header className="App-header">
<h1>This is big text!</h1>
</header>
<nav>
<NavLink to="/a">A</NavLink>
|
<NavLink to="/b">B</NavLink>
</nav>
<main>
{props.right}
</main>
</div>
</>
);
}
在反应路由器中的用法:
<Route path="myPath" element={
<Layout left={<p>I'm left</p>}
right={<p>I'm right</p>} />
} />