web-dev-qa-db-ja.com

React Routerを介して状態を渡すにはどうすればよいですか?

ここに私がトラブルを引き起こしているファイルがあります:

var Routers = React.createClass({

  getInitialState: function(){
    return{
      userName: "",
      relatives: []
    }
  },

  userLoggedIn: function(userName, relatives){
    this.setState({
      userName: userName,
      relatives: relatives,
    })
  },

  render: function() {
    return (
      <Router history={browserHistory}>
        <Route path="/" userLoggedIn={this.userLoggedIn} component={LogIn}/>
        <Route path="feed" relatives={this.state.relatives} userName={this.state.userName} component={Feed}/>
      </Router>
    );
  }
});

新しいthis.state.relativesおよびthis.state.userNameをルートを介して「フィード」コンポーネントに渡そうとしています。しかし、私はこのエラーメッセージを受け取っています:

警告:[react-router]変更できません;無視されます

なぜこれが起こるのかは知っていますが、状態を「フィード」コンポーネントに渡す方法がわからないのです。私は過去5時間この問題を修正しようとしており、かなり必死になっています!

助けてください!ありがとう


解決:

以下の回答は参考になり、私は支援者に感謝しますが、これは最も簡単な方法ではありませんでした。私の場合、これを行う最善の方法は次のとおりです。ルートを変更するときは、次のようにメッセージを添付するだけです。

browserHistory.Push({pathname: '/pathname', state: {message: "hello, im a passed message!"}});

または、リンクを介して行う場合:

<Link 
    to={{ 
    pathname: '/pathname', 
    state: { message: 'hello, im a passed message!' } 
  }}/>

ソース: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/location.md

到達しようとしているコンポーネントで、たとえば次のような変数にアクセスできます。

  componentDidMount: function() {
    var recievedMessage = this.props.location.state.message
  },

これがお役に立てば幸いです! :)

51
Jonas Bergner

tl; dr最善の策は、アプリケーション全体でアクセス可能な状態を管理するときにreduxmobxなどのストアを使用することです。これらのライブラリを使用すると、コンポーネントは状態に接続/状態を監視し、状態の変更を最新の状態に保つことができます。

<Route>とは何ですか?

<Route>コンポーネントを介してプロップを渡すことができない理由は、それらが何もレンダリングしないという意味で実際のコンポーネントではないためです。代わりに、ルート構成オブジェクトを構築するために使用されます。

つまり、これは:

<Router history={browserHistory}>
  <Route path='/' component={App}>
    <Route path='foo' component={Foo} />
  </Route>
</Router>

これと同等です:

<Router history={browserHistory} routes={{
  path: '/',
  component: App,
  childRoutes: [
    {
      path: 'foo',
      component: Foo
    }
  ]
}} />

ルートは最初のマウントでのみ評価されるため、新しい小道具を渡すことはできません。

静的小道具

ストアに渡す静的プロップがいくつかある場合は、ストアに挿入する独自の高次コンポーネントを作成できます。残念ながら、上記のように、<Route>sは一度しか評価されないため、これは静的プロップに対してのみ機能します。

function withProps(Component, props) {
  return function(matchProps) {
    return <Component {...props} {...matchProps} />
  }
}

class MyApp extends React.Component {
  render() {
    return (
      <Router history={browserHistory}>
        <Route path='/' component={App}>
          <Route path='foo' component={withProps(Foo, { test: 'ing' })} />
        </Route>
      </Router>
    )
  }
}

location.stateを使用

location.stateは、ナビゲーション中にコンポーネント間で状態を渡す便利な方法です。ただし、大きな欠点が1つあります。これは、アプリケーション内を移動するときにのみ状態が存在することです。ユーザーがあなたのウェブサイトへのリンクをたどる場合、その場所に状態は付加されません。

ストアを使用する

それでは、どのようにしてルートのcomponentsにデータを渡しますか?一般的な方法は、reduxmobxなどのストアを使用することです。 reduxを使用すると、より高次のコンポーネントを使用して、コンポーネントをストアにconnectできます。その後、ルートのcomponent(実際にはルートコンポーネントを子とするHOC)がレンダリングされると、ストアから最新の情報を取得できます。

const Foo = (props) => (
  <div>{props.username}</div>
)

function mapStateToProps(state) {
  return {
    value: state.username
  };
}

export default connect(mapStateToProps)(Foo)

私はmobxに特に精通していませんが、私の理解から、セットアップはさらに簡単になります。 reduxmobx、または他の状態管理のいずれかを使用すると、アプリケーション全体に状態を渡すことができます。

注:ここで読むのをやめることができます。以下は状態を渡すためのもっともらしい例ですが、おそらくストアライブラリを使用する必要があります。

ストアなし

ストアを使用したくない場合はどうしますか?運が悪いですか?いいえ。ただし、Reactの実験機能を使用する必要があります: contextcontextを使用するには、親コンポーネントの1つがgetChildContextメソッドとchildContextTypesオブジェクトを明示的に定義する必要があります。 contextを介してこれらの値にアクセスする子コンポーネントは、contextTypesオブジェクトを定義する必要があります(propTypesと同様)。

class MyApp extends React.Component {

  getChildContext() {
    return {
      username: this.state.username
    }
  }

}

MyApp.childContextTypes = {
  username: React.PropTypes.object
}

const Foo = (props, context) => (
  <div>{context.username}</div>
)

Foo.contextTypes = {
  username: React.PropTypes.object
}

<Route>contextsの小道具としてcomponent値を自動的に注入する独自の高次コンポーネントを作成することもできます。これは「貧乏人の店」のようなものでしょう。これを機能させることはできますが、前述のストアライブラリのいずれかを使用するよりも効率が悪く、バグが多い可能性があります。

React.cloneElementはどうですか?

<Route>componentに小道具を提供する別の方法がありますが、一度に1レベルしか機能しません。本質的に、Reactルーターが現在のルートに基づいてコンポーネントをレンダリングする場合、最も深くネストされた一致<Route>の要素を最初に作成します。次に、次に深くネストされた<Route>の要素を作成するときに、その要素をchildren propとして渡します。つまり、2番目のコンポーネントのrenderメソッドでは、React.cloneElementを使用して既存のchildren要素を複製し、それに追加の小道具を追加できます。

const Bar = (props) => (
  <div>These are my props: {JSON.stringify(props)}</div>
)

const Foo = (props) => (
  <div>
    This is my child: {
      props.children && React.cloneElement(props.children, { username: props.username })
    }
  </div>
)

これはもちろん、複数のレベルの<Route>componentsを介してこの情報を渡す必要がある場合、特に面倒です。 <Route>の親コンポーネントから状態を注入する方法がないため、ベース<Route path='/' component={Base}>コンポーネント(つまり、<Router>)内で状態を管理する必要もあります。

28
Paul S

ルーターコンポーネントがマウントされると、React-routerの状態を変更することはできません。独自のHTML5ルートコンポーネントを記述し、URLの変更をリッスンできます。

class MyRouter extend React.component {
   constructor(props,context){
   super(props,context);
   this._registerHashEvent = this._registerHashEvent.bind(this)

 } 

 _registerHashEvent() {
    window.addEventListener("hashchange", this._hashHandler.bind(this), false);
  }
  ...
  ...

render(){
  return ( <div> <RouteComponent /> </div>)
}
1
Khalid Azam

これはこの質問に答えるのが遅れる可能性があることを知っていますが、将来これについて疑問に思う人は、この例の方法で行うことができます:

export default class Routes extends Component {
constructor(props) {
    super(props);
    this.state = { config: 'http://localhost' };
}
render() {
    return (
        <div>
            <BrowserRouter>
                <Switch>
                    <Route path="/" exact component={App} />
                    <Route path="/lectures" exact render={() => <Lectures config={this.state.config} />} />
                </Switch>
            </BrowserRouter>
        </div>
    );
}

}

この方法で、Lectureコンポーネント内の構成小道具に到達できます。これは問題を少し回避しますが、最初はいい感じです! :)

1
Damian Busz