반응 라우터-소품을 핸들러 구성 요소에 전달


315

React Router를 사용하는 React.js 응용 프로그램의 구조는 다음과 같습니다 .

var Dashboard = require('./Dashboard');
var Comments = require('./Comments');

var Index = React.createClass({
  render: function () {
    return (
        <div>
            <header>Some header</header>
            <RouteHandler />
        </div>
    );
  }
});

var routes = (
  <Route path="/" handler={Index}>
    <Route path="comments" handler={Comments}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
);

ReactRouter.run(routes, function (Handler) {
  React.render(<Handler/>, document.body);
});

일부 속성을 Comments구성 요소 에 전달하고 싶습니다 .

(일반적으로 나는 이렇게 할 것이다 <Comments myprop="value" />)

React Router로 가장 쉽고 올바른 방법은 무엇입니까?


여기서의 문제, 이와 유사한 경우, 특히 일부 언어로 작성된 프레임 워크 또는 라이브러리의 경우 특정 조합 수단 (MoC)이 부족 합니다. React에서 프리미티브 는 괜찮아 보입니다 .React 요소와 React 요소와 MoC 컴포넌트에서 프리미티브로 컴포넌트를 정의하면 꽤 좋습니다 . 그러나 조합 수단 이 불완전합니다. 구성 요소를 다른 구성 요소와 결합하면서 구성 요소에 소품전달할 수 있어야합니다. 한 구성 요소를 다른 구성 요소 안에 자식으로 배치하거나 한 구성 요소를 다른 구성 요소에 소품으로 전달하는 것은 중요하지 않습니다.
Selçuk

같은 몇 가지 구문 <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> 또는 <ComponentA x={ComponentB(ComponentC()) } /> 그렇지 않으면이 추상화의 조합의 문제가 재발 등, 등 추상화 포장하는 기본 요소로 일류 시민, 어떤 일류 인식 수단이어야 등의 해결이라는 최적의 간접 솔루션에 비해 약간 덜 필요합니다.
Selçuk

답변:


152

최신 정보

새로운 릴리스 이후 Route래퍼를 사용하지 않고 구성 요소 를 통해 직접 소품을 전달할 수 있습니다 . 예를 들어 prop사용render 합니다.

구성 요소:

class Greeting extends React.Component {
  render() {
    const {text, match: {params}} = this.props;

    const {name} = params;

    return (
      <React.Fragment>
        <h1>Greeting page</h1>
        <p>
          {text} {name}
        </p>
      </React.Fragment>
    );
  }
}

용법:

<Route path="/greeting/:name" render={(props) => <Greeting text="Hello, " {...props} />} />

코드 및 상자 예


구 버전

내가 선호하는 방법은 Comments구성 요소를 감싸고 래퍼를 경로 처리기로 전달하는 것입니다.

다음은 변경 사항이 적용된 예입니다.

var Dashboard = require('./Dashboard');
var Comments = require('./Comments');

var CommentsWrapper = React.createClass({
  render: function () {
    return (
      <Comments myprop="myvalue"/>
    );
  }
});

var Index = React.createClass({
  render: function () {
    return (
      <div>
        <header>Some header</header>
        <RouteHandler/>
      </div>
    );
  }
});

var routes = (
  <Route path="/" handler={Index}>
    <Route path="comments" handler={CommentsWrapper}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
);

ReactRouter.run(routes, function (Handler) {
  React.render(<Handler/>, document.body);
});

58
같은 문제가 발생하지만이 솔루션이 빨리 장황하지 않습니까?
captDaylight

8
captDaylight에 동의하면 자세한 정보가됩니다. 이것을 처리하는 더 좋은 방법을 선호합니다!
Mattias Hallström

6
@mattiashallstrom IMO에서 1.0의 더 좋은 방법은 단순히 속성을 경로에 추가하는 것입니다. Thomas E의 답변을 참조하십시오.
k00k

28
당신은 또한 꽤 짧은 거기 상태 비 저장 구성 요소 구문 (단지 람다)를 추가 할 수 있습니다<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic

33
나는 "바람직한 방법"인 속성을 전달하기 위해 추가 구성 요소를 만드는 것에 동의하지 않을 것입니다. 그것은 장황하고 복잡하며 오류가 발생하기 쉽고 상상할 수있는 모든면에서 분명히 잘못되었습니다. 리 액트 라우터가 허용하는 유일한 방법 일 수도 있지만 "선호"라고 부르는 것은 확장입니다. 누가 선호합니까?
Szczepan Hołyszewski 2016 년

260

래퍼를 쓰지 않으려면 다음과 같이하십시오.

class Index extends React.Component { 

  constructor(props) {
    super(props);
  }
  render() {
    return (
      <h1>
        Index - {this.props.route.foo}
      </h1>
    );
  }
}

var routes = (
  <Route path="/" foo="bar" component={Index}/>
);

11
정답입니다. 반응 라우터 1.0에서는 route구성 요소에 일반 객체를 얻을 수 있습니다 . 다음은 GitHub의 문제의 답이다 : github.com/rackt/react-router/issues/615#issuecomment-100432086
ycdesu

4
이것은 내가 찾던 간단한 대답입니다. 다른 기술은 효과가 있지만 10 배의 코드가 필요합니다. v1.0.x에서 잘 작동합니다. 내가 볼 수있는 유일한 단점은 라우터 컨테이너 유무에 관계없이 동일한 구성 요소를 사용하려는 경우입니다. 그러나 나를 위해 모든 최상위 구성 요소는 경로와 일대일로 매핑되었습니다.
killthrush

12
감사합니다! 궁금한 점이 있으면 컴포넌트에서 foo 속성을 다음과 같이 사용할 수 있습니다. this.props.route.foo
k00k

8
혼란을 피하기 위해 정답으로 설정할 수 있습니까?
Archibald

2
아니! 실제 솔루션에 대한 Rajesh Naroth의 답변 참조 :)
Alex

114

허용 된 응답에서 ciantic 의 주석에서 복사 :

<Route path="comments" component={() => (<Comments myProp="value" />)}/>

이것은 제 생각에 가장 우아한 해결책입니다. 효과가있다. 나를 도와 주었다.


이것은 기본적으로 위의 래퍼 답변과 동일하지만 훨씬 덜 장황합니다. 그러나 사람은 그 구문이 짜증납니다. _ref
EdH

11
익명 래퍼와 같이 주입 된 모든 소품 (예 : 위치)이 누락되었습니다. 하나는 수동으로 소품을 전달해야 component={(props) => (<Comments myProp="value" location={ props.location } />)}하지만 모두 다시 지저분한 것입니다
yuji

1
@JacobThomason 우리는 라우터 구성을 다시 렌더링하지 않으므로 성능 저하가 발생하지 않습니다.
Sebastien Lorber

9
React-Router 4부터 인라인 기능을 제공하면 원하지 않는 재 마운트가 많이 발생합니다. 인라인 렌더링의 경우 렌더링 소품을 사용하십시오. 문서에 링크
Daniel Reina

1
@yuji 너무 지저분하지 않게하기 위해 할 수있는 일 : component={(props) => (<Comments {...props} myProp="value" />)}주입 된 소품을 유지하기
apelsinapa

57

이것은 yuji 가 불편한 의견을 남기지 않고 Rajesh솔루션으로 React Router 4 용으로 업데이트되었습니다.

코드는 다음과 같습니다.

<Route path="comments" render={(props) => <Comments myProp="value" {...props}/>}/>

render대신을 사용 합니다 component. 그 이유는 원하지 않는 재 장착 을 피하기 위함입니다 . 또한 props해당 메소드에 전달하고 , Object Spread 연산자 (ES7 제안)와 함께 Comments 구성 요소에 동일한 소품을 사용합니다.


원치 않는 마운팅도 해결하여 정말 좋습니다! +1
GLindqvist 2016

44

ColCh의 답변에 대한 후속 조치입니다. 구성 요소의 래핑을 추상화하는 것은 매우 쉽습니다.

var React = require('react');

var wrapComponent = function(Component, props) {
  return React.createClass({
    render: function() {
      return React.createElement(Component, props);
    }
  });
};

<Route path="comments" handler={wrapComponent(Comments, {myprop: value})}/>

이 솔루션을 아직 테스트하지 않았으므로 피드백이 중요합니다.

이 방법을 사용하면 라우터를 통해 전송 된 소품 (예 : 매개 변수)을 덮어 쓰거나 제거한다는 점에 유의해야합니다.


1
밥, 폐쇄에 대해 잘 알고 있습니까? stackoverflow.com/questions/111102/…
sigmus

3
그리고 라우터에서 쿼리와 매개 변수가 필요하면 다음과 같이 작동합니다 return React.createElement(Component, _.assign({}, this.props, props));(이것은 결합 된 객체를 구성하기 위해 _.assign을 사용합니다 ... 다른 방법도 가능합니다).
Malcolm Dwyer

2
아이들도 통과시키고 싶을 수도 있습니다. | var wrapComponent = function (Component, props) {return React.createClass ({render : function () {return React.createElement (Component, props, this.props.children);}}); };
Julio Rodrigues


1
NB 이것은 현재 React Router의 구 버전입니다. 현재 V4가있다 render, component그리고 children방법 Route. 로 유의 @dgrcode의 대답은 지적 사용해야하는 render대신component
icc97

31

소품을 <RouteHandler>v0.13.x에서 또는 v1.0에서 Route 구성 요소 자체에 전달하여 소품을 전달할 수 있습니다 .

// v0.13.x
<RouteHandler/>
<RouteHandler someExtraProp={something}/>

// v1.0
{this.props.children}
{React.cloneElement(this.props.children, {someExtraProp: something })}

( https://github.com/rackt/react-router/releases/tag/v1.0.0 의 업그레이드 안내서에서 )

모든 하위 처리기는 동일한 소품 세트를 받게됩니다. 상황에 따라 유용 할 수도 있고 그렇지 않을 수도 있습니다.


1
실제로 React.cloneElement여러 요소가 전달되는 것을 보는 것은 당황 하지만 함수 서명 은 하나의 반응 요소 만 취하는 것 같습니다. 이 스 니펫을 이해하기 쉽게 만들 수 있다고 생각합니다.
manu

2
이것은 분명히 문서의 목표에 대한 가장 좋은 대답이지만, 나는 더 나은 사용법을 보여주기 위해 manu에 동의합니다. : 질문에 코드보다 구체적으로는 같을 것이다 React.cloneElement(this.props.children, {myprop: "value"})또는 React.cloneElement(this.props.children, {myprop: this.props.myprop})
juanitogan

이 답변이 이깁니다. 라우터가 당신을 위해하는 일들 가운데서 무슨 일이 일어나고 있는지 훨씬 더 명확합니다. 누군가 코드를 읽을 때 주석이 색인 내부에 있다는 것을 알고 있다면 색인으로 어떤 소품이 주석으로 전송되는지 확인합니다. Comments가 라우터 처리기라는 것을 알게되면 라우터 구성을 살펴보고 Index parent 주석을 찾아서 계속 살펴 보겠습니다.
mjohnsonengr

24

ES6을 사용하면 구성 요소 래퍼를 인라인으로 만들 수 있습니다.

<Route path="/" component={() => <App myProp={someValue}/>} >

자녀를 합격시켜야하는 경우 :

<Route path="/" component={(props) => <App myProp={someValue}>{props.children}</App>} >


이것은 좋지만 경우에 따라 어린이를 통과하지 않습니다.
zpr

@zpr 저는 props.children에 대한 예제를 추가했습니다
Nick

2
으로 @dgrcode의 대답은 지적, 당신은 사용해야하는 render대신component
icc97

23

반응 라우터 v4 알파

이전 방법과 매우 유사하지만 새로운 방법이 있습니다.

import { Match, Link, Miss } from 'react-router';
import Homepage from './containers/Homepage';

const route = {
    exactly: true,
    pattern: '/',
    title: `${siteTitle} - homepage`,
    component: Homepage
  }

<Match { ...route } render={(props) => <route.component {...props} />} />

PS 이것은 알파 버전에서만 작동하며 v4 알파 릴리스 이후에 제거되었습니다. v4 최신에서는 경로와 정확한 소품으로 다시 한 번입니다.

react-lego 예제 앱은 react-router-4 브랜치의 route.js에서 정확히 이것을 수행하는 코드를 포함합니다


21

다음은 가장 깨끗한 솔루션입니다 (React Router v4).

<Route
  path="/"
  component={props => <MyComponent {...props} foo="lol" />}
/>

MyComponent아직이 props.matchprops.location하고있다 props.foo === "lol".


13

상태 비 저장 함수 구성 요소로 감싸십시오.

<Router>
  <Route 
    path='/' 
    component={({children}) => 
      <MyComponent myProp={'myVal'}>{children}</MyComponent/>
    }/>
</Router>

12

또한 래퍼 구성 요소를 피하고보다 쉽게 ​​부모의 상태를 소품으로 전달하기 위해 RouteHandler 믹스 인을 사용할 수 있습니다.

var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var RouteHandler = require('react-router/modules/mixins/RouteHandler');

var Index = React.createClass({
      mixins: [RouteHandler],
      render: function () {
        var handler = this.getRouteHandler({ myProp: 'value'});
        return (
            <div>
                <header>Some header</header>
                {handler}
           </div>
        );
  }
});

var routes = (
  <Route path="/" handler={Index}>
    <Route path="comments" handler={Comments}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
);

ReactRouter.run(routes, function (Handler) {
  React.render(<Handler/>, document.body);
});

1
그것은 글로벌 bower 빌드에 ReactRouter.RouteHandlerMixin과 같이 공개적으로 노출되어 있으므로 그렇게 생각하지 않습니다.
jul

또한 래퍼 메서드를 사용하여 작업 할 수없는 TransitionGroup 및 CSSTransitionGroup을 사용하여 전환에 애니메이션을 적용 할 수 있습니다.
jul

2
공식 문서에는 언급이 없다는 것이 이상합니다.
코스 메 티카

더 이상 React 문서
믹스 인을

12

다음 <RouterHandler/>과 같은 방법으로 소품을 전달할 수 있습니다 .

var Dashboard = require('./Dashboard');
var Comments = require('./Comments');

var Index = React.createClass({
  render: function () {
    var props = this.props; // or possibly this.state
    return (
        <div>
            <header>Some header</header>
            <RouteHandler {...props} />
        </div>
    );
  }
});

이것의 단점은 소품을 무차별 적으로 전달한다는 것입니다. 따라서 Comments경로 구성에 따라 실제로 다른 구성 요소를위한 소품을받을 수 있습니다. props불변 이기 때문에 큰 문제 는 아니지만 두 개의 다른 구성 요소가 이름이 foo있지만 값이 다른 소품을 기대하는 경우 문제가 될 수 있습니다 .


1
이 코드에서 3 개의 마침표 또는 점은 무엇을 의미합니까?{...props}
Giant Elk

2
Flux는 부모 앱에서 경로로 상태를 보내지 않아도됩니다. 위의 코드가 작동하지만 명시 적으로 보이지 않으므로 숨겨진 majic이 추악하고 추적하기가 쉽지 않습니다.
자이언트 엘크

2
연산자 설명을 전파하십시오 . 명시 적이지는 않지만 불변의 소품을 전달하기 때문에 최악의 것은 아닙니다.
Meistro April

스프레드 연산자에 대한 ReactJS 문서는 다음과 같습니다. facebook.github.io/react/docs/jsx-spread.htmlfacebook.github.io/react/docs/transferring-props.html
Giant Elk

이것은 나를 위해 일했지만 올바른 구문은 {... this.props}입니다
Win

10

1.0 및 2.0에서는 createElementprop of Router를 사용하여 대상 요소를 정확하게 작성하는 방법을 지정할 수 있습니다 . 설명서 소스

function createWithDefaultProps(Component, props) {
    return <Component {...props} myprop="value" />;
}

// and then    
<Router createElement={createWithDefaultProps}>
    ...
</Router>

6

es6 및 stateless 함수 를 결합 하여 훨씬 더 깨끗한 결과를 얻을 수 있습니다.

import Dashboard from './Dashboard';
import Comments from './Comments';

let dashboardWrapper = () => <Dashboard {...props} />,
    commentsWrapper = () => <Comments {...props} />,
    index = () => <div>
        <header>Some header</header>
        <RouteHandler />
        {this.props.children}
    </div>;

routes = {
    component: index,
    path: '/',
    childRoutes: [
      {
        path: 'comments',
        component: dashboardWrapper
      }, {
        path: 'dashboard',
        component: commentsWrapper
      }
    ]
}

정확히 어떻게 작동하는지 잘 모르겠지만 잘못 보입니다. this.props함수에서 사용 하고 있는데 작동하지 않을 것이라고 확신합니다. 확장 기능 대신 순수 함수를 사용 하는 경우 인수 React.Component로 전달해야합니다 props. 구성 요소 및 소품
icc97

6

리 액트 라우터 v 4 솔루션

나는 오늘 일찍이 질문에 걸려 넘어졌으며 여기에 내가 사용하는 패턴이 있습니다. 잘하면 이것은 최신 솔루션을 찾는 사람에게 유용합니다.

이것이 최선의 해결책 인지 확실하지 않지만 이것이 나의 현재 패턴입니다. 일반적으로 자주 사용하는 구성 요소를 관련 구성 (로더, 모달 등)으로 유지하는 Core 디렉토리가 있으며 다음과 같은 파일을 포함합니다.

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

const getLocationAwareComponent = (component) => (props) => (
  <Route render={(routeProps) => React.createElement(component, 
{...routeProps, ...props})}/>
)

export default getLocationAwareComponent

그런 다음 해당 파일에서 다음을 수행합니다.

import React from 'react'
import someComponent from 'components/SomeComponent'
import { getLocationAwareComponent } from 'components/Core/getLocationAwareComponent'
const SomeComponent = getLocationAwareComponent(someComponent)

// in render method:
<SomeComponent someProp={value} />

구성 요소의 기본 내보내기를 겸손한 낙타 케이스로 가져 오면 CamelCase에서 새로운 위치 인식 구성 요소의 이름을 지정하여 정상적으로 사용할 수 있습니다. 추가 가져 오기 라인과 할당 라인 이외의 구성 요소는 예상대로 작동하고 모든 경로 소품을 추가하여 모든 소품을 정상적으로받습니다. 따라서 this.props.history.push ()를 사용하여 구성 요소 수명주기 메소드에서 행복하게 리디렉션하고 위치를 확인할 수 있습니다.

도움이 되었기를 바랍니다!


4

반응 라우터 2.x의 경우.

const WrappedComponent = (Container, propsToPass, { children }) => <Container {...propsToPass}>{children}</Container>;

그리고 당신의 경로에서 ...

<Route path="/" component={WrappedComponent.bind(null, LayoutContainer, { someProp })}>
</Route>

3 번째 매개 변수가 다음과 같은 객체인지 확인하십시오 { checked: false }.


1

React Router의 문제점은 컴포넌트를 렌더링하여 소품 전달을 중단한다는 것입니다. 반면 내비게이션 라우터를 사용하면 고유 한 구성 요소를 렌더링 할 수 있습니다. 즉, 다음 코드와 함께 JsFiddle 쇼 와 같이 소품을 전달하기 위해 농구 대를 뛰어 넘을 필요가 없습니다 .

var Comments = ({myProp}) => <div>{myProp}</div>;

var stateNavigator = new Navigation.StateNavigator([
  {key:'comments', route:''}
]);

stateNavigator.states.comments.navigated = function(data) {
  ReactDOM.render(
    <Comments myProp="value" />,
    document.getElementById('content')
  );
}

stateNavigator.start();

1

Rajesh Naroth 답변을 기반으로 라우터 유무에 관계없이 구성 요소를 사용하십시오.

class Index extends React.Component {

  constructor(props) {
    super(props);
  }
  render() {
    const foo = (this.props.route) ? this.props.route.foo : this.props.foo;
    return (
      <h1>
        Index - {foo}
      </h1>
    );
  }
}

var routes = (
  <Route path="/" foo="bar" component={Index}/>
);

또는 당신은 이런 식으로 할 수 있습니다 :

export const Index = ({foo, route}) => {
  const content = (foo) ? foo : (route) ? route.foo : 'No content found!';
  return <h1>{content}</h1>
};

0

반응 라우터 2.5.2의 경우 솔루션이 너무 쉽습니다.

    //someConponent
...
render:function(){
  return (
    <h1>This is the parent component who pass the prop to this.props.children</h1>
    {this.props.children && React.cloneElement(this.props.children,{myProp:'value'})}
  )
}
...

0

사용자 지정 경로 구성 요소를 사용하면 React Router v3에서 가능합니다.

var Dashboard = require('./Dashboard');
var Comments = require('./Comments');
var routes = (
  <Route path="/" handler={Index}>
    <MyRoute myprop="value" path="comments" handler={Comments}/>
    <DefaultRoute handler={Dashboard}/>
  </Route>
);

에 관해서는 <MyRoute>구성 요소 코드, 그것은 뭔가를 같이해야한다 :

import React from 'react';
import { Route } from 'react-router';
import { createRoutesFromReactChildren } from 'react-router/lib//RouteUtils';

const MyRoute = () => <div>&lt;MyRoute&gt; elements are for configuration only and should not be rendered</div>;

MyRoute.createRouteFromReactElement = (element, parentRoute) => {
    const { path, myprop } = element.props;
    // dynamically add crud route
    const myRoute = createRoutesFromReactChildren(
        <Route path={path} />,
        parentRoute
    )[0];
    // higher-order component to pass myprop as resource to components
    myRoute.component = ({ children }) => (
        <div>
            {React.Children.map(children, child => React.cloneElement(child, { myprop }))}
        </div>
    );
    return myRoute;
};

export default MyRoute;

사용자 지정 경로 구성 요소 접근 방식에 대한 자세한 내용은 다음 주제에 대한 내 블로그 게시물을 확인하십시오. http://marmelab.com/blog/2016/09/20/custom-react-router-component.html


0

이것은 아마도 쿠키 핸들러와 함께 react-router-dom을 사용하는 가장 좋은 방법입니다

index.js에서

import React, { Component } from 'react'
import {Switch,Route,Redirect} from "react-router-dom"
import {RouteWithLayout} from "./cookieCheck"

import Login from "../app/pages/login"
import DummyLayout from "../app/layouts/dummy"
import DummyPage from "../app/pages/dummy" 

export default ({props})=>{
return(
    <Switch>
        <Route path="/login" component={Login} />
        <RouteWithLayout path="/dummy" layout={DummyLayout} component={DummyPage} 
        {...props}/>
        <Redirect from="/*" to="/login" />
    </Switch>
  )
}

cookieCheck를 사용하십시오

import React , {createElement} from 'react'
import {Route,Redirect} from "react-router-dom"
import {COOKIE,getCookie} from "../services/"

export const RouteWithLayout = ({layout,component,...rest})=>{
    if(getCookie(COOKIE)==null)return <Redirect to="/login"/>
        return (
        <Route {...rest} render={(props) =>
            createElement(layout, {...props, ...rest}, createElement(component, 
      {...props, ...rest}))
       }
      />
    )
}

0
class App extends Component {
  constructor(props){
    super(props);

    this.state = {
      data:null
    }


  }
 componentDidMount(){
   database.ref().on('value', (snapshot) =>{
     this.setState({
       data : snapshot.val()
      })
   });
 }

  render(){
  //  const { data } = this.state
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path = "/" component = { LandingPage }  />
        <Route 
          path='/signup' 
          render = { () => <Signup  data = {this.state.data} />} />
        </Switch>
    </BrowserRouter>

  );
  }
};

export default App;

0

위와 같은 솔루션을 사용하면 v3.2.5에서 작동합니다.

<Route
  path="/foo"
  component={() => (
    <Content
      lang="foo"
      meta={{
        description: lang_foo.description
      }}
    />
  )}
/>

또는

<Route path="/foo">
  <Content
    lang="foo"
    meta={{
      description: lang_foo.description
    }}
  />
</Route>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.