prosource

여러 레이아웃으로 리액트 라우터 v4

probook 2023. 3. 19. 18:20
반응형

여러 레이아웃으로 리액트 라우터 v4

퍼블릭 레이아웃 내에서 루트 중 일부를 렌더링하고 프라이빗 레이아웃 내에서 다른 루트를 렌더링하고 싶은데 깔끔한 방법이 있을까요?

분명히 효과가 없는 예이지만, 제가 찾고 있는 것을 대략적으로 설명해 주셨으면 합니다.

<Router>

  <PublicLayout>
    <Switch>
      <Route exact path="/" component={HomePage} />
      <Route exact path="/about" component={AboutPage} />
    </Switch>
  </PublicLayout>

  <PrivateLayout>
    <Switch>
      <Route exact path="/profile" component={ProfilePage} />
      <Route exact path="/dashboard" component={DashboardPage} />
    </Switch>
  </PrivateLayout>

</Router>

레이아웃을 특정 경로로 전환하고 싶은데 새로운 리액트 라우터에서 어떻게 전환해야 합니까?

루트를 네스트 하면 기능하지 않게 되어, 다음의 에러가 표시됩니다.

You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored

편집: 레이아웃이 루트 그룹 전체를 랩하도록 하는 것은 동일한 프라이빗/퍼블릭 루트 그룹에 있는 한 레이아웃이 한 번만 렌더링됨을 의미합니다.예를 들어 레이아웃을 사용하여 각 페이지를 레이아웃으로 감싸면 페이지가 변경될 때마다 레이아웃이 서버에서 무언가를 가져와야 하는 경우에는 큰 문제가 됩니다.

이를 위해 Route 컴포넌트(레이아웃)에 추가 속성을 추가하는 간단한 컴포넌트를 만듭니다.

function RouteWithLayout({layout, component, ...rest}){
  return (
    <Route {...rest} render={(props) =>
      React.createElement( layout, props, React.createElement(component, props))
    }/>
  );
}

그러면 당신의 경우 당신의 경로는 다음과 같습니다.

<Switch>
    <RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
    <RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
    <RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
    <RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
</Switch>

업데이트 2020

지금은 이 방법을 따르고 있습니다.이전에는 간단하게 투고했습니다.

const Pages = () => {
  return (
    <ReactRouter>
      <Switch>
        <Route path="/comingsoon" component={ComingSoon} exact />
        <Route>
          <MainLayout>
            <Switch>
              <Route path="/home" exact>
                <Home />
              </Route>
              <Route path="/login" exact>
                <Login />
              </Route>
              <Route path="/useraccount" exact>
                <UserAccount />
              </Route>
              <Route path="/createaccount" exact>
                <CreateAccount />
              </Route>
              <Route path="/contact" exact>
                <Contact />
              </Route>
              <Route path="/about" exact>
                <About />
              </Route>
              <Redirect path="/" exact to="/comingsoon" />
              <Route path="*" exact component={NotFound} />
            </Switch>
          </MainLayout>
        </Route>
      </Switch>
    </ReactRouter>
  );
};

이와 같이 Main Layout은 곧 나올 페이지를 제외한 모든 것을 처리합니다.

오래된 답변

Typescript 를 사용하고 있는 경우, 다음의 리액트 레이아웃을 따를 필요가 있는 경우는, 다음과 같이 레이아웃을 선언할 수 있습니다.

import './Default.less';

import React from 'react';
import { Route } from "react-router-dom";

import { Sider } from './Components';
import { Notification } from 'Client/Components';

interface IDefaultProps {
  component: any
  path?: string;
  exact?: boolean;
}

const Default: React.SFC<IDefaultProps> = (props) => {
  const { component: Component, ...rest } = props;
  return <Route {...rest} render={matchProps => (
    <div className="defaultLayout">
      <Sider />
      <div className="defaultLayoutContent">
        <Component {...matchProps} />
      </div>
      <Notification />
    </div>
  )} />
}

export default Default;

그리고 다음과 같이 루트를 선언합니다.

import React from 'react';
import { Route } from 'react-router-dom';

import { DefaultLayout } from 'Client/Layout';
import { Dashboard, Matters, Schedules, Students } from './Containers';

export const routes = <div>
  <DefaultLayout exact path="/" component={Dashboard} />
  <DefaultLayout path="/matters" component={Matters} />
  <DefaultLayout path="/schedules" component={Schedules} />
  <DefaultLayout path="/students" component={Students} />
</div>;

2019+

검색 후 깨끗하고 효율적인 방법(남용적인 재렌더링 방지):

    <Route exact path={["/about", "/"]}>
      <PublicLayout>
        <Route exact path="/" component={HomePage} />
        <Route path="/about" component={AboutPage} />
      </PublicLayout>
    </Route>
    <Route path={["/profile", "/dashboard"]}>
      <PrivateLayout>
        <Route path="/profile" component={ProfilePage} />
        <Route path="/dashboard" component={DashboardPage} />
      </PrivateLayout>
    </Route>

aslo, 리팩터링 할 수 있습니다.완전한 답변을 참조해 주세요.https://stackoverflow.com/a/57358661/3437790

경로 파일에 레이아웃이 없는 것 같아요.

경로를 깨끗한 상태로 유지합니다.

<Route exact path="/" component="HomePage" />

★★★★★★★★★★★★★★★★★★★★★★.HomePage렌더링된 내용을 선택한 레이아웃으로 줄 바꿈:

...
render() {
  <PublicLayout>
    <h1>Home page!</h1>
  </PublicLayout>
}

이렇게 하면 루트는 매우 깨끗한 상태를 유지할 수 있습니다.또, 양쪽의 레이아웃(예를 들면 404 페이지)을 서포트하는 루트를 간단하게 표시할 수 있습니다.

이 질문이 나온 곳마다 글을 쓰도록 하겠습니다. 혹시 다른 곳에서 보셨다면 죄송합니다.한동안 힘들었기 때문에 하고 있는 것일 뿐이고, 가능한 한 이 솔루션을 보급할 가치가 있다고 생각합니다.말은 충분해, 이게 내가 한 일이야, 내 생각엔 꽤 자기 설명적인 것 같아.마법처럼 작동하며 타이핑도 매우 쉽습니다.

const withLayout = (LayoutComp, ComponentComp) => <LayoutComp><ComponentComp /></LayoutComp>;
const withDefaultLayout = (ComponentComp) => () => withLayout(Layout, ComponentComp);
const withEmptyLayout = (ComponentComp) => () => withLayout(EmptyLayout, ComponentComp);

export const routes = <div>
    <Switch>
        <Route path="/" exact render={withDefaultLayout(Home)} />
        <Route path='/subscriptions' render={withDefaultLayout(SubscriptionsWrapped)} />

        <Route path='/callback' render={withEmptyLayout(AuthCallback)} />
        <Route path='/welcome/initial' render={withEmptyLayout(Start)} />
    </Switch>
</div>;

Florians의 답변을 시험해 보았습니다만, 각 루트에 대해 Layout의 개별 인스턴스가 생성되므로 탭 슬라이딩과 같은 내비게이션 전환은 모두 중지됩니다.

좀 더 우아한 솔루션을 원하지만 v4에서 앱을 다시 사용할 수 있게 되었습니다.

루트를 랩할 대상을 결정하는 컴포넌트를 가리키는 하나의 루트를 정의합니다.

<Router>
  <Route component={AppWrap} />
</Router>

AppWrap에서 다음과 같은 작업을 수행합니다.

 var isPrivateLayout;
 for (var path of listOfPrivateLayoutPaths) {
   if (this.props.location.pathname == path) {
     isPrivateLayout= true;
   }
 }
 if (isPrivateLayout) {
   return <PrivateLayout>
        (routes)
      </PrivatelyLayout>
 } else {
   return <PublicLayout>
        (routes)
      </PublicLayout>;
 }

Route Config는 이를 보다 명확하게 표현하기 위해 사용할 수 있습니다.

업데이트: 다른 방법으로 해결했습니다만, 앱의 다른 부분을 /app 또는 /admin으로 네임스페이스를 지정하도록 강요하는 경우.

UserRoutes, AdminRoutes 및 PublicRoutes의 각 컴포넌트는 기본적으로 큰 스위치컴포넌트로 특정 레이아웃이 루트에 있습니다.

그 외관은 다음과 같습니다.

<Router>
  <Switch>
    <Route path="/app" render={props => <UserRoutes {...props} />} />
    <Route path="/admin" render={props => <AdminRoutes {...props} />} />
    <Route path="/" render={props => <PublicRoutes {...props} />} />
  </Switch>
</Router>

구식: 각 경로의 렌더 소품을 사용하는 것이 하나의 해결책이지만, 매우 번거로운 것 같습니다.

<Router>
  <Switch>
    <Route
      path="/"
      render={() => <PublicLayout><HomePage /></PublicLayout>}
    />
    <Route
      path="/about"
      render={() => <PublicLayout><AboutPage /></PublicLayout>}
    />
    <Route
      path="/profile"
      render={() => <PrivateLayout><ProfilePage /></PrivateLayout>}
    />
    <Route
      path="/dashboard"
      render={() => <PrivateLayout><DashboardPage /></PrivateLayout>}
    />
  </Switch>
</Router>

아이디어는 Zaptree와 동일하지만 es6 구문을 사용하고 react-router의 Route 컴포넌트 대신 사용할 수 있도록 체크를 추가했습니다.

새 컴포넌트(예: /src/components/Route/index.js)를 만듭니다.

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Route as ReactRoute} from 'react-router'

class Route extends Component {
  static propTypes = {
    component: PropTypes.func.isRequired,
    layout: PropTypes.func,
    path: PropTypes.string,
    exact: PropTypes.bool
  }

  render = () => {
    const {component, layout, path, exact} = this.props
    let routeComponent = props => React.createElement(component, props)

    if (layout) {
      routeComponent = props =>
        React.createElement(layout, props, React.createElement(component, props))
    }

    return <ReactRoute path={path} exact={exact} render={routeComponent}/>
  }
}

export default Route

작성한 Route 컴포넌트를 사용합니다.

import Route from 'components/Route/'
...

<Router history={createHistory()}>
  <Switch>
    <Route exact path='/' layout={PublicLayout} component={HomePage}/>
    <Route exact path='/' layout={PrivateLayout} component={ProfilePage}/>
    <Route path='/logins' component={Login}/>
  </Switch>
</Router>

Route 컴포넌트의 렌더 프로포넌트를 사용합니다.이렇게 해도 루트 변경 시마다 레이아웃컴포넌트를 마운트 및 마운트 할 수 없습니다.자세한 내용은 여기를 참조하십시오.

2개의 레이아웃 컴포넌트 Primary.js와 Secondary.js가 있다고 합니다.

App.js 렌더링 방법에서는 반환만 하면 됩니다.

<BrowserRouter>
   <Switch>
     <Route path='/login' render={() => <Secondary><Login/></Secondary>} />
     <Route path='/dashboard' render={() => <Primary><Dashboard/></Primary>} />
   </Switch>
</BrowserRouter>

세분화하기 위해 페이지 구성요소를 레이아웃으로 줄바꿈하는 고차 구성요소 레이아웃 구성요소를 정의할 수도 있습니다.(테스트되지 않음)

<Route to='/login' render={() => Secondary(Login)}

@Qop은 맞습니다만, 새로운 리액트라우터에서는 스위치 내부에 루트 패스가 첫 번째 루트로 설정되어 있는 경우 루트 패스는 항상 일치하므로 다음 루트는 표시되지 않습니다.루트 경로를 마지막에 넣어야 합니다.

<Switch>
    <RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
    <RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
    <RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
    <RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
</Switch>

@Zaptree와 같은 아이디어

레이아웃

function PublicLayout(props) {
  return (
      <Route {...props} />
  );
}

function PrivateLayout(props) {
  return (
      <Route {...props} />
  );
}

루트

<Switch>
    <PublicLayout exact path="/" component={HomePage} />
    <PrivateLayout path="/profile" component={ProfilePage} />
    <Route path="/callback" component={NoLayoutPage} />
</Switch>

이 솔루션은 효과가 있습니다.

<Router>
  <Switch>
    <PublicLayout>
      <Route exact path="/" component={HomePage} />
      <Route exact path="/about" component={AboutPage} />
    </PublicLayout>
  </Switch>       

  <Switch>
    <PrivateLayout>
      <Route exact path="/profile" component={ProfilePage} />
      <Route exact path="/dashboard" component={DashboardPage} />
    </PrivateLayout>
  </Switch>    
</Router>

언급URL : https://stackoverflow.com/questions/42862028/react-router-v4-with-multiple-layouts

반응형