web-dev-qa-db-fra.com

Le composant ne remonte pas lorsque les paramètres de route changent

Je travaille sur une application de réaction en utilisant react-router. J'ai une page de projet qui a une URL comme suit:

myapplication.com/project/unique-project-id

Lorsque le composant de projet est chargé, je déclenche une demande de données pour ce projet à partir de l'événement composantDidMount. Je suis maintenant confronté à un problème où si je bascule directement entre deux projets, seul l'identifiant change de la sorte ...

myapplication.com/project/982378632
myapplication.com/project/782387223
myapplication.com/project/198731289

composantDidMount n'est pas déclenché à nouveau, donc les données ne sont pas actualisées. Existe-t-il un autre événement de cycle de vie que je devrais utiliser pour déclencher ma demande de données ou une stratégie différente pour traiter ce problème?

58
Constellates

Si la liaison est dirigée vers le même itinéraire avec juste un paramètre différent, il ne s'agit pas de remonter, mais de recevoir de nouveaux accessoires. Vous pouvez donc utiliser la fonction componentWillReceiveProps(newProps) et rechercher newProps.params.projectId.

Si vous essayez de charger des données, je vous recommande de les récupérer avant que le routeur ne gère la correspondance à l'aide de méthodes statiques sur le composant. Découvrez cet exemple. Réagissez Mega Demo . De cette façon, le composant chargerait les données et se mettrait automatiquement à jour lorsque les paramètres de route changeraient sans qu'il soit nécessaire de s'appuyer sur componentWillReceiveProps.

60
Brad Bumbalough

Si vous avez besoin d'un remontage de composant lorsque la route change, vous pouvez transmettre une clé unique à l'attribut de clé de votre composant (la clé est associée à votre chemin/route). Ainsi, chaque fois que la route changera, la clé modifiera également le déclencheur du composant React pour démonter/remonter. J'ai eu l'idée de cette réponse

63
wei

Voici ma réponse, semblable à certaines des précédentes mais avec du code.

<Route path="/page/:pageid" render={(props) => (
  <Page key={props.match.params.pageid} {...props} />)
} />
28
Breakpoint25

Vous devez être clair sur le fait qu'un changement d'itinéraire ne provoquera pas d'actualisation de page, vous devez le gérer vous-même.

import theThingsYouNeed from './whereYouFindThem'

export default class Project extends React.Component {

    componentWillMount() {

        this.state = {
            id: this.props.router.params.id
        }

        // fire action to update redux project store
        this.props.dispatch(fetchProject(this.props.router.params.id))
    }

    componentDidUpdate(prevProps, prevState) {
         /**
         * this is the initial render
         * without a previous prop change
         */
        if(prevProps == undefined) {
            return false
        }

        /**
         * new Project in town ?
         */
        if (this.state.id != this.props.router.params.id) {
           this.props.dispatch(fetchProject(this.props.router.params.id))
           this.setState({id: this.props.router.params.id})
        }

    }

    render() { <Project .../> }
}
10
chickenchilli

Voici comment j'ai résolu le problème:

Cette méthode récupère l'élément individuel de l'API:

loadConstruction( id ) {
    axios.get('/construction/' + id)
      .then( construction => {
        this.setState({ construction: construction.data })
      })
      .catch( error => {
        console.log('error: ', error);
      })
  }

J'appelle cette méthode de composantDidMount, cette méthode ne sera appelée qu'une fois, lorsque je chargerai cette route pour la première fois:

componentDidMount() {   
    const id = this.props.match.params.id;
    this.loadConstruction( id )
  }

Et de composantWillReceiveProps qui sera appelé depuis la deuxième fois, nous chargeons le même itinéraire, mais un ID différent, et j'appelle la première méthode pour recharger l'état, puis le composant charge le nouvel élément.

componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.id !== this.props.match.params.id) {
      const id = nextProps.match.params.id
      this.loadConstruction( id );
    }
  }
4
Oscar Jovanny

Si tu as:

<Route
   render={(props) => <Component {...props} />}
   path="/project/:projectId/"
/>

Dans React 16.8 et les versions ultérieures, à l'aide des crochets , vous pouvez effectuer les opérations suivantes:

import React, { useEffect } from "react";
const Component = (props) => {
  useEffect(() => {
    props.fetchResource();
  }, [props.match.params.projectId]);

  return (<div>Layout</div>);
}
export default Component;

Dans ce cas, vous ne déclenchez un nouvel appel fetchResource que lorsque props.match.params.id change.

3
Alfonso Pérez

La réponse de @ wei fonctionne très bien, mais dans certaines situations, il est préférable de ne pas définir de clé de composant interne, mais d’acheminer la route elle-même. De plus, si le chemin d'accès au composant est statique, mais que vous souhaitiez simplement que le composant soit remonté à chaque fois que l'utilisateur y accède (par exemple, pour effectuer un appel API à composantDidMount ()), il est pratique de définir location.pathname sur key of route. Avec cet itinéraire, tout son contenu est remonté lorsque l'emplacement change.

const MainContent = ({location}) => (
    <Switch>
        <Route exact path='/projects' component={Tasks} key={location.pathname}/>
        <Route exact path='/tasks' component={Projects} key={location.pathname}/>
    </Switch>
);

export default withRouter(MainContent)
1
Paulus Limma

Sur la base des réponses de @wei, @ Breakpoint25 et @PaulusLimma, j'ai créé ce composant de remplacement pour le <Route>. Cela remontera la page lorsque l'URL changera, forçant ainsi tous les composants de la page à être créés et montés à nouveau, pas seulement à être restitués. Tous les componentDidMount() et tous les autres points d'ancrage de démarrage sont également exécutés lors de la modification de l'URL.

L'idée est de changer la propriété des composants key lorsque l'URL change, ce qui oblige React à remonter le composant.

Vous pouvez l'utiliser comme remplacement immédiat pour <Route>, par exemple comme ceci:

<Router>
  <Switch>
    <RemountingRoute path="/item/:id" exact={true} component={ItemPage} />
    <RemountingRoute path="/stuff/:id" exact={true} component={StuffPage} />
  </Switch>
</Router>

Le composant <RemountingRoute> est défini comme suit:

export const RemountingRoute = (props) => {
  const {component, ...other} = props
  const Component = component
  return (
    <Route {...other} render={p => <Component key={p.location.pathname + p.location.search}
                                              history={p.history}
                                              location={p.location}
                                              match={p.match} />}
    />)
}

RemountingRoute.propsType = {
  component: PropTypes.object.isRequired
}

Cela a été testé avec React-Router 4.3.

1
Juha Syrjälä