In React si possono usare varie librerie per l'accesso ad API remote, nell'esempio utilizzo la Fetch API nata per sostituire l'oggetto XMLHttpRequest presente negli attuali browser.
Per il codice base vedi React come funziona.
In questo esempio visualizzo l'elenco dei comuni della lombardia, prendendo i dati da una API locale ( Cerca comuni italiani), questo è il codice completo:
React JSX
function Waiting(props){
  //lo stile nel codice è solo un esempio, meglio isolarlo in un file css/less/sass a parte
  const myStyle = {
    color: "#fff",
    fontWeigth: "bold",
    backgroundColor: "#080",
    paddingLeft: "5px",
    paddingRight: "5px",
    marginLeft: "10px"
  };

  return <span>
    { props.show > 0 &&
      <span style={myStyle}>Wait ...</span>
    }
  </span>
}

function Messages(props){
  // quando elenco una serie di items il tag html deve avere
  // la property "key" univoca
  console.log("Messages: " + JSON.stringify(props));
  return <ul>
    { props.messages.map( (item, index) => { 
        return <li key={index}><strong>{item}</strong> - <small>{new Date().toString() }</small></li>  
      })
    }
  </ul>
}

function List(props) {
  const myStyle = {
    height: "100px",
    overflow: "auto",
    border: "1px solid #ccc"
  };

  const rows = props.items.map( item =>{
    //Each child in an array or iterator should have a unique "key" prop.
    return <li key={item.id}>{item.comune} ( {item.cap} )</li>
  });

  return <div style={ myStyle }>
    <ul>{ rows }</ul>
  </div>
}

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      waiting: 0,
      errors:[],
      items: []
    }

    this.url = "comuni.json";

    this.handleReload = this.handleReload.bind(this);
  }

  componentDidMount() {
    //carico i dati all'inizio
    this.loadItems();
  }

  handleReload() {
    console.log("handleReload");
    this.loadItems();
  }

  setWaiting(){
    console.log("setWaiting: " + this.state.waiting);
    this.setState( prevState => { 
      return { 
        waiting: prevState.waiting + 1,
        errors: [],
        items: []
      } 
    });
  }

  loadingCompleted(data){
    console.log("resetWaiting: " + this.state.waiting);
    this.setState( prevState => { 
      return { 
        waiting: prevState.waiting > 0 ? prevState.waiting-1 : 0,
        items: data
      }
    });
  }

  setError(ex){
    console.log("setError: " + ex);
    this.setState( prevState => { 
      const newErrors = [...prevState.errors, ex.message]; 
      return {
        waiting: prevState.waiting > 0 ? prevState.waiting-1 : 0,
        errors: newErrors
      }
    });
  }

  loadItems(){
    console.log("loadItems init");
    //inizio caricamento
    this.setWaiting();
    console.log("loadItems waiting");

    fetch(this.url)
      .then( response => {
        if(!response.ok){
          throw Error("Network request failed");
        }
        return response.json();
      }) 
      .then( json => {
        console.log("loadItems json");
        const data = json.data.map( (item, index)  => {
          return {
            id: index,
            cap: item.cap,
            comune: item.comune
          }
        });
        this.loadingCompleted(data);
        console.log("loadItems ok");
      })
      .catch( ex => {
        console.log("loadItems catch:" + ex);
        //fine caricamento
        this.setError(ex);
      });
  }

  render() {
    console.log("E: " + JSON.stringify(this.state.errors));
    return <div>
      <button onClick={this.handleReload}>Reload</button>
      <Waiting show={ this.state.waiting } />
      <Messages messages={ this.state.errors } />
      <List items={ this.state.items } />
    </div>
  }
}

//bootstrap
ReactDOM.render(
  <App />,
  document.getElementById("root")
);
e questo il risultato:


Per questo esempio come prima cosa ho creato un componente principale chiamato App dove nel costruttore ho definito lo stato dell'applicazione:
React JSX
constructor(props) {
  super(props);

  this.state = {
    waiting: 0,
    errors:[],
    items: []
  }

  this.url = "comuni.json";
  //this.url = "/api/app/comuni?q=LOM&f=codreg";

  this.handleReload = this.handleReload.bind(this);
}
lo stato contiene 3 proprietà:
  • waiting: conteggio delle chiamate asincrone in corso, se maggiore di 0 visualizza il messaggio Wait... ad indicare che è in corso un operazione asincrona (vedi componente Waiting)
  • errors: array di messaggi di testo con gli errori generati dall'applicazione (vedi componente Messages)
  • items: array di elementi da visualizzare, caricati tramite Fetch API (vedi componente List)
successivamente ho definito dei metodi per gestire lo stato di caricamento e gli errori:
  • setWaiting: incrementa il contatore this.state.waiting
  • loadingCompleted: decrementa il contatore this.state.waiting e aggiorna l'array this.state.items
  • setError: imposta un messaggio di errore in this.state.errors
gli items vengono caricati sul caricamento del componente tramite il metodo componentDidMount che a sua volta chiama this.loadItems. Questo metodo si occuopa di recuperare i dati dalla API tramite la Fetch API:
React JSX
//inizio la chiamata alle API
fetch(this.url)
  .then( response => {
    if(!response.ok){
      //sollevo un eccezione se ho errori di comunicazione (vedi catch)
      throw Error("Network request failed");
    }
    //se va tutto bene converto la risposta in json
    return response.json();
  }) 
  .then( json => {
    //ok, ho la risposta in json
    //non uso direttamente l'oggetto come risposta,
    //costruisco un altro oggetto con i dati nel formato che serve all'applicazione
    //in questo modo mi svincolo dal formato della API, eventuali modifiche del formato della API andranno gestite solo qui
    const data = json.data.map( (item, index)  => {
      return {
        id: index,
        cap: item.cap,
        comune: item.comune
      }
    });
    //notifico a React che i ho nuovi dati
    this.loadingCompleted(data);
  })
  .catch( ex => {
    //fine caricamento e impostazione errore
    this.setError(ex);
  });
map è una nuova funzione delle specifiche ES6 che permette di eseguire, per ogni item di un array, una funzione di trasformazione e ritornare un nuovo array.
Come ultima azione nel componente App gestisco il metodo render:
React JSX
render() {
  return <div>
    <button onClick={this.handleReload}>Reload</button>
    <Waiting show={ this.state.waiting } />
    <Messages messages={ this.state.errors } />
    <List items={ this.state.items } />
  </div>
}

Nell'applicazione vengono usati alcuni componenti. Il primo è Waiting e si occupa di visualizzare il messaggio Wait..., il suo scopo è indicare l'esecuzione di una o più chiamate asincrone:
React JSX
function Waiting(props){
  const myStyle = { .. vedi codice iniziale . };

  return <span>
    { props.show > 0 &&
      <span style={myStyle}>Wait ...</span>
    }
  </span>
}
Il componente si asppetta che venga passata una proprietà show che rappresenta il numero di chiamate asincrone in corso (this.state.waiting).

L'altro componente, visualizza gli eventuali errori dell'applicazione ed è Messages:
React JSX
function Messages(props){
  const myStyle = { ... };

  return <ul style={myStyle}>
    { props.messages.map( (item, index) => { 
        return <li key={index}><strong>{item}</strong> - <small>{new Date().toString() }</small></li>  
      })
    }
  </ul>
}
Al componente ho dato un nome generico, in quanto con opportune modifiche potrebbe visualizzare anche messaggi informativi oltre a quelli di errore.
Si aspetta che venga passata nella proprietà messages un array di messaggi stringa (this.state.errors).
Quando si cicla su una serie di elementi che vanno inseriti nel DOM, React vuole che ogni elemento abbia un identificativo univoco, questo identificativo è rappresentato dalla proprietà key.

L'ultimo componente è List e si occupa di visualizzare gli elementi che sono stati memorizzati in this.state.items e passati alla proprietà items del componente:
React JSX
function List(props) {
  const myStyle = { ... };

  const rows = props.items.map( item =>{
    //Each child in an array or iterator should have a unique "key" prop.
    return <li key={item.id}>{item.comune} ( {item.cap} )</li>
  });

  return <div style={ myStyle } className="elenco">
    <ul>{ rows }</ul>
  </div>
}
Faccio notare l'uso della proprietà className in camelCase anziché la classica class usata in html
Per questi ultimi 3 componenti, non avendo bisogno di gestire lo stato locale ad ogni componente, li ho stati creati tramite la keyword function anziché class.
Potrebbe interessarti anche: