【问题标题】:Is it possible to use multiple outlets in a component in React-Router V6是否可以在 React-Router V6 的组件中使用多个插座
【发布时间】:2025-12-20 13:05:06
【问题描述】:

我在应用程序中使用 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>
  );
}

这样的事情可能吗?理想情况下,我希望除了标题之外还有其他一些可以通过这种方式控制的道具,所以这样的变化的良好组织系统会很棒。

【问题讨论】:

    标签: reactjs react-hooks react-router react-router-dom


    【解决方案1】:

    最直接的方法是将 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>
    

    【讨论】:

    • 谢谢,这实际上是我用来设置标题的解决方案。我问这个问题是因为我现在想添加第二个参数(我有一个带有几个按钮的“操作”部分......我希望按钮的样式/位置相同,但哪些按钮应该是路由道具)。我想我可以将它双重包装在 TitleProvider 和 ActionProvider 中,但如果它继续扩展,这似乎会很快变得混乱
    • @DMAR 这就是为什么我在最后一篇关于如何更抽象地考虑这两个组件的简介。当然,使用单独的 React 上下文分离关注点是最佳的,但我担心包装。为了使代码更加DRY,您可以声明一个PageWrapper 组件,该组件访问所有自定义上下文挂钩并获取所有道具,对于它们,结果看起来很像上面的路由.这是否有意义,或者您希望我使用更通用的示例解决方案进行更新?
    • 啊,我想我明白你现在在说什么了,谢谢!我会尽力而为,但我想我知道如何应用它了!
    • @DMAR 酷,如果您在这里有任何其他问题或需要任何东西,请随时寻求澄清/帮助。干杯。
    • v6 提供了自己的钩子,称为useOutletContext,用于在父母和孩子之间共享状态或内容:reactrouter.com/docs/en/v6/api#useoutletcontext
    【解决方案2】:

    我在左右布局中遇到了同样的问题:更改侧边栏内容和主要内容,而无需重复样式、横幅等。

    我发现的最简单的方法是删除嵌套路由,并创建一个布局组件,在其中我通过属性提供不断变化的内容。

    布局组件(为这篇文章剥离):

    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>
              &nbsp;|&nbsp;
              <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>} />
    } />
    

    【讨论】:

      最近更新 更多