web-dev-qa-db-fra.com

Comment puis-je passer de l'état à travers React Router?

Voici le fichier qui me cause des problèmes:

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>
    );
  }
});

J'essaie de faire passer les nouveaux this.state.relatives et this.state.userName par les routes dans mon composant "feed". Mais je reçois ce message d'erreur:

Avertissement: [réagir-routeur] Vous ne pouvez pas changer; il sera ignoré

Je sais pourquoi cela se produit, mais je ne sais pas comment je suis supposé passer les états à mon composant "feed". J'essaie de résoudre ce problème depuis 5 heures et je suis de plus en plus désespéré!

S'il vous plaît aider! Merci


SOLUTION:

Les réponses ci-dessous ont été utiles et je remercie les athors, mais ce n’était pas la façon la plus simple de le faire. La meilleure façon de le faire dans mon cas s’est avéré être la suivante: lorsque vous modifiez des itinéraires, vous y attachez simplement un message comme ceci:

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

ou si vous le faites via un lien:

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

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

Dans le composant que vous essayez d'atteindre, vous pouvez ensuite accéder à la variable comme ceci, par exemple:

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

J'espère que ça aide! :)

51
Jonas Bergner

tl; dr votre meilleur choix est d'utiliser un magasin tel que redux ou mobx lors de la gestion d'un état devant être accessible dans votre application. Ces bibliothèques permettent à vos composants de se connecter à/d'observer l'état et d'être tenus au courant de tout changement d'état.

Qu'est-ce qu'un <Route>?

La raison pour laquelle vous ne pouvez pas transmettre d'accessoires via les composants <Route> est qu'ils ne sont pas de véritables composants en ce sens qu'ils ne rendent rien. Au lieu de cela, ils sont utilisés pour créer un objet de configuration de route.

Cela signifie que ceci:

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

est équivalent à ceci:

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

Les itinéraires ne sont évalués que sur le montage initial. C'est pourquoi vous ne pouvez pas leur transmettre de nouveaux accessoires.

Accessoires statiques

Si vous souhaitez transmettre des accessoires statiques à votre magasin, vous pouvez créer votre propre composant d'ordre supérieur qui les injectera dans le magasin. Malheureusement, cela ne fonctionne que pour les accessoires statiques car, comme indiqué ci-dessus, les <Route>s ne sont évalués qu'une seule fois.

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>
    )
  }
}

Utilisation de location.state

location.state est un moyen pratique de passer d'un état à l'autre lorsque vous naviguez. Cependant, il présente un inconvénient majeur, à savoir que l'état n'existe que lors de la navigation dans votre application. Si un utilisateur suit un lien vers votre site Web, aucun état n'est associé à l'emplacement.

Utiliser un magasin

Alors, comment transmettez-vous les données aux components de votre route? Une méthode courante consiste à utiliser un magasin tel que redux ou mobx. Avec redux, vous pouvez connect votre composant dans le magasin à l'aide d'un composant d'ordre supérieur. Ensuite, lorsque la variable component de votre route (qui est en réalité le HOC avec votre composant de route comme enfant) s'affiche, elle peut récupérer les informations mises à jour à partir du magasin.

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

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

export default connect(mapStateToProps)(Foo)

Je ne connais pas très bien mobx, mais si je comprends bien, il peut être encore plus facile à configurer. L'utilisation de redux, mobx ou de l'un des autres systèmes de gestion d'état constitue un excellent moyen de passer d'un état à un autre dans votre application.

Remarque: Vous pouvez arrêter de lire ici. Vous trouverez ci-dessous des exemples plausibles d’état de passage, mais vous devriez probablement simplement utiliser une bibliothèque de magasin.

Sans magasin

Et si vous ne voulez pas utiliser un magasin? Avez-vous de la chance? Non, mais vous devez utiliser une fonction expérimentale de React: the context . Pour utiliser context, l'un de vos composants parents doit définir explicitement une méthode getChildContext ainsi qu'un objet childContextTypes. Tout composant enfant souhaitant accéder à ces valeurs via context doit ensuite définir un objet contextTypes (similaire à 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
}

Vous pouvez même écrire votre propre composant d'ordre supérieur qui injecte automatiquement les valeurs context sous forme d'accessoires de votre <Route>components. Ce serait un peu un "magasin du pauvre homme". Vous pourriez le faire fonctionner, mais probablement moins efficacement et avec plus de bogues que l’utilisation de l’une des bibliothèques de magasins susmentionnées.

Qu'en est-il de React.cloneElement?

Il existe un autre moyen de fournir des accessoires au component d'un <Route>, mais cela ne fonctionne que sur un niveau à la fois. En règle générale, lorsque React Router rend les composants en fonction de l'itinéraire actuel, il crée d'abord un élément pour le <Route> le plus imbriqué et le plus apparié. Il passe ensuite cet élément en tant que propriété children lors de la création d'un élément pour le prochain <Route> le plus profondément imbriqué. Cela signifie que dans la méthode render du deuxième composant, vous pouvez utiliser React.cloneElement pour cloner l'élément children existant et y ajouter des accessoires supplémentaires.

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>
)

Cela est bien sûr fastidieux, surtout si vous deviez transmettre cette information à plusieurs niveaux de <Route>components. Vous devez également gérer votre état dans votre composant <Route> de base (c'est-à-dire <Route path='/' component={Base}>) car vous ne pouvez pas injecter l'état à partir des composants parents du <Router>.

28
Paul S

Vous ne pouvez pas modifier l'état du routeur React une fois le composant du routeur monté. Vous pouvez écrire votre propre composant de route HTML5 et écouter les modifications d'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

Je sais qu'il est peut-être tard pour répondre à cette question, mais si vous vous posez des questions à ce sujet à l'avenir, vous pouvez utiliser l'exemple suivant:

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>
    );
}

}

De cette façon, vous pouvez accéder aux accessoires de configuration dans le composant Lecture. C'est une petite promenade autour de la question, mais pour commencer c'est une bonne touche! :)

1
Damian Busz