react-router - передайте реквізит до компонента обробника


315

У мене така програма для React.js за допомогою маршрутизатора React :

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?


Проблема тут, і в подібних подібних випадках, особливо з рамками або libs, написаними в деяких мовах, певна відсутність засобів комбінації (MoC). Примітиви здаються нормальними в React, вони досить хороші, визначаючи компоненти з примітивами, в елементах React та компоненті, MoC , що здається нормальним, як і в React. Але засоби комбінування неповні. Повинно бути здатним передавати реквізит компоненту , поєднуючи компонент з іншим, неважливо, розміщуючи один компонент всередині іншого компонента як дочірнього або передаючи один компонент як реквізит іншому.
Сельчук

З таким синтаксисом, як <ComponentA x={<ComponentB y={<ComponentC z={} />} />} /> АБО <ComponentA x={ComponentB(ComponentC()) } /> В іншому випадку, ці проблеми комбінацій абстракцій повторюватимуться і потребуватимуть деяких менш оптимальних та непрямих рішень, званих обхідних шляхів, таких як обгортання тощо, тощо.
Сельчук

Відповіді:


152

ОНОВЛЕННЯ

З нового випуску можна передавати реквізит безпосередньо через Routeкомпонент, не використовуючи Wrapper. Наприклад, використовуючи 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, він стає багатослівним. Я вважаю за краще кращий спосіб впоратися з цим!
Маттіас Холлстрьом

6
@mattiashallstrom IMO, кращий спосіб 1.0 - просто додати властивість до маршруту. Дивіться відповідь Томаса Е.
k00k

28
ви можете додати там синтаксис компонента без громадянства (просто лямбда), це досить коротко<Route path="comments" component={() => (<Comments myProp="value" />)}/>
Ciantic

33
Я ніколи не погоджуюся зі створенням додаткового компонента просто для передачі властивостей, які є "бажаним способом". Це багатослівний, складний, схильний до помилок і просто відвертий у будь-якому розумінні. Це, можливо, дозволяє лише маршрутизатор реагування, але називати його "кращим" - це розтягнення. Кращим хто?
Szczepan Hołyszewski

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/isissue/615#issuecomment-100432086
ycdesu

4
Це проста відповідь, яку я шукав. Інші методи працюють, але вимагають в 10 разів більше коду. Добре працює для v1.0.x. Єдиний недолік, який я можу побачити - це якщо ви маєте намір використовувати один і той же компонент як з контейнером маршрутизатора, так і без нього. Але для мене всі мої компоненти найвищого рівня відображали маршрути 1 на 1 з маршрутами.
вбивство

12
Щиро дякую! Якщо хтось цікавиться, властивість foo буде доступною у вашому компоненті як: this.props.route.foo
k00k

8
Чи можна вказати правильну відповідь, щоб уникнути плутанини?
Арчібальд

2
Ні! Дивіться відповідь Раєша Нарота для реального рішення :)
Алекс

114

Копіювання цитатів із коментарів у прийнятій відповіді:

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

Це, на мою думку, найвитонченіше рішення. Це працює. Допомогли мені.


Це в основному те саме, що відповідь обгортки вище, але набагато менш багатослівна. Людина, однак, цей синтаксис смокче. Спробуйте кинути в_ref
EdH

11
Це як анонімна обгортка, тому всі введені реквізити (наприклад, місце розташування) пропущені. Треба вручну передавати реквізити на кшталт, component={(props) => (<Comments myProp="value" location={ props.location } />)}але все знову стає безладно
липня

1
@JacobThomason, ми не відтворюємо конфігурацію маршрутизатора, що реагує, так що це навряд чи буде покаранням за ефективність.
Себастьян Лорбер

9
Станом на React-Router 4, якщо ви надаєте вбудовану функцію, ви отримаєте багато небажаних перерахунків. Для вбудованого візуалізації використовуйте рендер. Посилання на документи
Даніель Рейна

1
@yuji Щоб не зробити це занадто безладним, можна зробити: component={(props) => (<Comments {...props} myProp="value" />)}підтримувати введений реквізит
apelsinapa

57

Це рішення від Rajesh , без незручного коментаря yuji та оновлене для React Router 4.

Код був би таким:

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

Зауважте, що я використовую renderзамість component. Причина - уникати небажаних перерахунків . Я також передаю propsцей метод, і я використовую ті самі реквізити компонента Comments з оператором розповсюдження об'єкта (пропозиція ES7).


Дійсно чудово і для вирішення небажаного монтажу! +1
GLindqvist

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 для складання об'єднаного об'єкта ... інші методи, звичайно, доступні).
Малькольм Дуайер

2
Ви можете також передати дітей var wrapComponent = функція (компонент, реквізит) {return React.createClass ({render: function () {return React.createElement (компонент, реквізит, this.props.children);}}); };
Хуліо Родрігес

1
Це компонент вищого порядку для тих, хто не знайомий з ними facebook.github.io/react/docs/higher-order-components.html
icc97

1
Примітка: Це для старої версії React Router зараз. Поточний v4 має render, componentі childrenметоди Route. Зауважте, що як вказує відповідь @dgrcode , ви повинні використовувати renderзамістьcomponent
icc97

31

Ви можете передавати реквізити, передаючи їх у <RouteHandler>(в v0.13.x) або сам компонент Route в v1.0;

// 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як передаються кілька елементів, але підпис функції, здається, бере лише один реагуючий елемент. Я думаю, цей фрагмент можна зробити простішим для розуміння.
ман

2
Це, очевидно, найкраща відповідь на цілі з документами, але я також погоджуюся з Ману, що це може бути написано для кращого використання. Код, більш конкретний для питання, виглядатиме так: React.cloneElement(this.props.children, {myprop: "value"})або React.cloneElement(this.props.children, {myprop: this.props.myprop})інше
juanitogan

Ця відповідь виграє. Крім того, набагато зрозуміліше, що відбувається посеред того, що робить маршрутизатор для вас. Коли хтось читає код, якщо я знаю, що коментарі знаходяться всередині індексу, я перегляну індекс, щоб побачити, які реквізити надсилаються до коментарів. Якщо я випадково знаю, що Comments - це обробник маршрутизатора, я перегляну конфігурацію маршрутизатора і з’ясую, що батьки індексують коментарі та все ще йду шукати.
mjohnsonengr

24

За допомогою ES6 ви можете просто зробити компонентні обгортки вбудованими:

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

Якщо вам потрібно передати дітей:

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


Це добре, але це не проходить через дітей, якщо вони є.
zpr

@zpr Я додав приклад для props.children
Нік

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 , наприклад, додаток містить код, який саме це робить у route.js на його відділенні react-router-4


21

Ось найчистіше рішення, яке я придумав (React Router v4):

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

MyComponentдо сих пір props.matchі props.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
Це публічно виставляється на глобальній збірці шару як ReactRouter.RouteHandlerMixin, тому я не думаю, що так.
липень

Це також дозволяє анімувати переходи за допомогою TransitionGroup та CSSTransitionGroup, які я не зміг приступити до роботи за допомогою методу обгортки.
липень

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}
Гігантський лось

2
Я думаю, що Flux уникне необхідності надсилати стан від батьківського додатка до маршруту. Наведений вище код працює, але він не є явним, тому прихований манікюр некрасивий, непросто простежити і відстежувати, що відбувається.
Гігантський лось

2
Поширення пояснення оператора . Це не явно, але це не найгірше, оскільки ви передаєте непорушний реквізит.
Мейстро

Ось документи ReactJS про оператора розповсюдження: facebook.github.io/react/docs/jsx-spread.html, а також facebook.github.io/react/docs/transferring-props.html
Гігантський Елк

це працювало для мене, але правильний синтаксис - {... this.props}
Win

10

В 1.0 і 2.0 ви можете використовувати createElementопору Routerдля вказівки, як саме створити ваш цільовий елемент. Джерело документації

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

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

6

Ви також можете комбінувати es6 та функції без стану, щоб отримати набагато більш чистий результат:

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як аргумент, див. Документи React на компоненти та реквізити
icc97

6

Рішення React Router v 4

Я натрапив на це питання раніше сьогодні, і ось такий шаблон я використовую. Сподіваємось, це корисно для всіх, хто шукає більш сучасного рішення.

Я не впевнений, чи це найкраще рішення, але це моя сьогоднішня схема. У мене зазвичай є основна директорія, в якій я зберігаю часто використовувані компоненти з відповідними конфігураціями (завантажувачі, модалі тощо), і я включаю такий файл:

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, щоб я міг його нормально використовувати. Крім додаткової лінії імпорту та рядка призначення, компонент поводиться так, як очікувалося, і отримує всі свої реквізити нормально, додаючи всі реквізити маршруту. Таким чином, я можу із задоволенням переадресувати з методів компонентного життєвого циклу за допомогою цього.props.history.push (), перевірити місце розташування тощо.

Сподіваюсь, це допомагає!


4

Для маршрутизатора реагування 2.x.

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

і на ваших маршрутах ...

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

переконайтеся , що треті пари є об'єктом , як: { checked: false }.


1

Проблема з маршрутизатором React полягає в тому, що він відображає ваші компоненти і таким чином зупиняє вас вставляти реквізити. Навігації маршрутизатор , з іншого боку, дозволяє зробити ваші власні компоненти. Це означає, що вам не доведеться стрибати через будь-які обручі, щоб передати реквізит як наступний код та супровідне шоу 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

це, мабуть, найкращий спосіб використовувати reakct-router-dom з обробником файлів cookie

в 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>
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.