1
votes

React - Les données Ajax ne sont pas transmises au composant enfant

J'ai deux composants, un parent et un enfant. J'utilise la méthode fetch dans le rappel de componentDidMount () . Une fois que j'ai fait cela, j'ai défini l'état des éléments clés sur les données extraites de l'API. Une fois que je fais cela, il devrait pouvoir être connecté à la console dans le composant enfant en tant que accessoire. Cependant, cela ne fonctionne pas. Qu'est-ce que je fais mal ici?

Composant parent:

import React, {Component} from 'react';

class Map extends Component {

    constructor(props) {
        super(props);
        console.log(props.items)
    }

    render () {
        return (
            <h1>{this.props.name}</h1>
        )
    }
}

export default Map;

Composant enfant:

import React, {Component} from 'react';
import Map from './maps/Map';

class Main extends Component {
    constructor(props){
        super(props);
        this.state = {
            name: "John",
            items: []
        }
    }

    componentDidMount() {
        fetch('https://hn.algolia.com/api/v1/search?query=')
        .then(dat => dat.json())
        .then(dat => {
            this.setState({
                items: dat.hits
            })
        })
    }

    render() {
        return (
            <div>
                <Map list={this.state.name} items={this.state.items}></Map>
            </div>
        )
    }
}

export default Main;


5 commentaires

faire render () {console.log (this.props.items) return (

{this.props.name}

)}


Il le fait ici stackblitz.com/edit/react-aywmmj


Oui, tu as raison. Pourquoi attrape-t-il console.log dans le rendu et non dans le constructeur? Ne devrait-il pas faire dans les deux? C'est un élément fondamental que je ne peux pas enrouler autour de ma tête


regardez l'exemple que j'ai posté ci-dessus


Votre paramètre s'appelle list = donc le nom n'est pas défini dans le composant Map . Quant au constructeur, il s'exécute lorsque vous créez le composant pour la première fois, mais lorsqu'il effectue un nouveau rendu pour recevoir plus d'accessoires, le constructeur n'est pas appelé à nouveau.


3 Réponses :


2
votes

Premièrement, fetch est asynchrone. Ainsi, l'instruction fetch peut être en attente au moment où vous essayez de console.log le résultat dans le constructeur enfant.

Mettre le console.log dans la méthode render fonctionnerait, car le composant sera rendu si l'état items change.


4 commentaires

donc ce que vous dites est que seule la méthode de rendu peut attraper un changement asynchrone dans les accessoires? Puis-je ne pas attraper le changement asynchrone ailleurs dans le composant enfant ???


Non, ce n'est pas ce que je dis. Un appel asynchrone n'est pas géré comme des instructions séquentielles. Dans votre cas, mettre le console.log à l'intérieur du render fonctionne, car l'ensemble du composant sera rendu si un état change. Le constructeur n'est appelé qu'une seule fois, lorsque l'objet est construit / instancié.


Alors, que dois-je faire pour pouvoir effectuer le changement d'accessoire dans le constructeur? À partir de maintenant, la console n'enregistre rien dans le constructeur après l'appel asynchrone.


Si vous voulez pouvoir utiliser la propriété async à l'intérieur du constructeur enfant, vous devez être sûr que l'enfant est instancié lorsque l'extraction est terminée. Vous pouvez utiliser les Portails de React pour cela.



1
votes

Le constructeur d'un composant ne s'exécute qu'une seule fois au cours d'un cycle de vie. Quand c'est le cas, props.items n'est pas défini car votre requête ajax est en cours, donc console.log (props.items) n'affiche rien.

Si vous changez votre constructeur en console.log ("built"); , vous verrez une sortie unique (les extraits de pile peuvent ne pas montrer cela - regardez dans la console de votre navigateur). Désormais, componentDidUpdate () peut être utilisé pour voir les nouveaux accessoires qui ont été définis lorsque votre demande ajax se termine.

Vous pouvez également enregistrer les accessoires dans la méthode render , qui s'exécutera une fois avant la résolution de la requête ajax et à nouveau après lorsque props.items changera.

Comme point secondaire, vous avez mais le composant essaie de rendre this.props.name , qui n'est pas défini .

De plus, si vous ne faites rien dans le constructeur (état d'initialisation ou fonctions de liaison) comme ici, vous n'en avez pas besoin.

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
class Map extends React.Component {
  constructor(props) {
    super(props);
    console.log("constructed");
  }
  
  componentDidUpdate(prevProps, prevState) {
    const props = JSON.stringify(this.props, null, 2);
    console.log("I got new props", props);
  }

  render() {
    return ( 
      <div>
        <h1>{this.props.name}</h1>
        <pre>
          <ul>
            {this.props.items.map((e, i) => 
              <li key={i}>{JSON.stringify(e, null, 2)}</li>)}
          </ul>
        </pre>
      </div>
    );
  }
}

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {name: "John", items: []};
  }

  componentDidMount() {
    fetch('https://hn.algolia.com/api/v1/search?query=')
      .then(dat => dat.json())
      .then(dat => {
        this.setState({items: dat.hits})
      });
  }

  render() {
    return ( 
      <div>
        <Map 
          name={this.state.name} 
          items={this.state.items}
        />
      </div>
    );
  }
}

ReactDOM.render(<Main />, document.body);


2 commentaires

Merci. C'est ce que je voulais savoir. Que le constructeur de Child n'attrape pas le changement de la récupération ajax du parent car il n'est probablement appelé qu'une seule fois. Puis-je définir l'état du composant enfant sur les accessoires dans componentDidUpdate ()?


Oui, mais vous avez besoin d'une condition pour empêcher les relances infinies. Consultez la documentation relative à componentDidMount . De plus, copier directement les accessoires dans l'état est un anti-modèle, également décrit dans la documentation.



1
votes

Le seul problème que vous rencontrez est que vous essayez d'utiliser this.props.name et les accessoires de votre composant Map sont appelés list et items , donc il retournera undefined .

Si vous connectez vos accessoires dans le constructeur, vous obtiendrez l'état initial de Main car le fetch n'a encore rien retourné. N'oubliez pas que le constructeur ne s'exécute qu'une seule fois. Donc, vous obtenez probablement un tableau vide lorsque vous connectez props.items dans le constructeur car c'est ce que vous avez dans votre état initial.

import CircularProgress from "@material-ui/core/CircularProgress"

class Main extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "John",
      items: [],
      fecthed: false
    };
  }

  componentDidMount() {
    fetch("https://hn.algolia.com/api/v1/search?query=")
      .then(dat => dat.json())
      .then(dat => {
        this.setState({
          items: dat.hits,
          fecthed: true
        });
      });
  }

  render() {
    return (
      <Map
        fetched={this.state.fecthed}
        list={this.state.name}
        items={this.state.items}
      />
    );
  }
}

class Map extends Component {
  render() {
    return (
      <div>
        {this.props.fetched ? (
          <div>
            <h1>{this.props.list}</h1>
            {this.props.items.map((item, indx) => (
              <div key={indx}>Author: {item.author}</div>
            ))}
          </div>
        ) : (
          <CircularProgress />
        )}
      </div>
    );
  }
}

Si vous enregistrez les accessoires dans votre méthode de rendu, vous verrez votre tableau rempli avec les données que vous avez récupérées, comme vous pouvez le voir ici: https://codesandbox.io/s/stoic-cache-m7d43 p >

Si vous ne souhaitez pas afficher le composant tant que les données ne sont pas récupérées, vous pouvez inclure une propriété booléenne dans votre état que vous définissez sur true une fois que la récupération renvoie une réponse et la transmet comme accessoire à votre composant. Votre composant peut vous utiliser cette variable pour afficher, par exemple, une double flèche pendant que vous récupérez les données. Voici un exemple: https://codesandbox.io/s/reverent-edison-in9w4 p >

{
  name: "John",
  items: []
}

J'espère que cela vous aidera. Bravo!


1 commentaires

Merci d'avoir pris le temps de nous aider. Cela a aidé oui.