Reaccionar sin volver a renderizar componentes después de usar setState

11 minutos de lectura

Avatar de usuario de Winson Bi
winson bi

Estoy intentando usar React para crear un sitio web con un mapa de Google que vuelve a dibujar las polilíneas después de cambiar el estado. Estoy usando setState() para establecer el estado correctamente, y puedo ver que los valores cambian cuando imprimo el objeto desde this.state en la consola, pero mis polilíneas en el mapa no se vuelven a representar y cambian de color.

Este es el componente principal que estoy usando para el proyecto, incluye un mapa y una tabla, renderMap() es lo que realmente genera el mapa y update_congestion_lines es lo que cambia el estado y espero activar una nueva representación.

import React, { Component } from 'react';
import {Map, Marker, GoogleApiWrapper, InfoWindow, Polyline} from 'google-maps-react';

class HomepageMap extends Component{
 static defaultProps = {
   center: {
       lat: 33.980530,
       lng: -117.377020
   },
   zoom: 11,
   style: {
     width: '100%',
     height: '100%'
   }
 };

 constructor(props){
   super(props);
   this.state = {
     error: null,
     cctvs: [],
     cctv_objects: [],
     center: {
       lat: 33.980530,
       lng: -117.377020
     },
     style: {
       width: '100%',
       height: '100%'
     },
     zoom: 11,
     showingInfoWindow : false,
     selectedPlace: {},
     activeMarker: {},
     image_path : [],
   };
   this.update_congestion_lines = this.update_congestion_lines.bind(this);
   this.grabColor = this.grabColor.bind(this);
   this.handleClick = this.handleClick.bind(this);
 }

 componentDidMount() {
   var url = "http://highwayanalytics.us/api/cctv?format=json&county=Riverside,San+Bernardino";
   fetch(url)
   .then(res => res.json())
   .then(
   (result) => {
       var list = [];
       for(var i = 0; i < result.length; i++){
           var cctv = result[i];
           if(cctv.image_url !== "Not Reported"){
           list.push(cctv);
           }
       }
       this.setState({
           cctvs: list,
           error: false
       });
   },
   (error) => {
       //console.log(error);
       this.setState({
           cctvs: [],
           error: true
       })
   });

   url = "http://highwayanalytics.us/api/graph?format=json&county=Riverside,San+Bernardino";
   fetch(url)
   .then(res => res.json())
   .then(
   (result) => {
     var list = [];
     for(var key in result){
       if(result.hasOwnProperty(key)){
         var val = result[key];
         var i = 0;
         if(key === "SR-60" || key === "I-10" || key === "SR-91" || key === "I-210"){
           for(i=0;i < val.length;i++){
             var prev_cctv = null;
             if(i!== 0){
               prev_cctv=val[i-1];
             }
             var next_cctv = null;
             if(i !== (val.length-1)){
               next_cctv = val[i+1];
               //Calc distance
               var prev_lat_midpoint = null;
               var prev_long_midpoint = null;
               var next_lat_midpoint = null;
               var next_long_midpoint = null;
               var temp = null;
               if(prev_cctv !== null){
                 if(prev_cctv.latitude > val[i].latitude){
                     temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
                     prev_lat_midpoint = val[i].latitude + temp;
                 }
                 else{
                     temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
                     prev_lat_midpoint = val[i].latitude - temp;
                 }
                 if(prev_cctv.longitude > val[i].longitude){
                    temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
                    prev_long_midpoint = val[i].longitude + temp;
                 }
                 else{
                    temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
                    prev_long_midpoint = val[i].longitude - temp;
                 }
               }
               if(next_cctv !== null){
                 if(next_cctv.latitude > val[i].latitude){
                   temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
                   next_lat_midpoint = val[i].latitude + temp;
                 }
                 else{
                   temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
                   next_lat_midpoint = val[i].latitude - temp;
                 }
                 if(next_cctv.longitude > val[i].longitude){
                   temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
                   next_long_midpoint = val[i].longitude + temp;
                 }
                 else{
                   temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
                   next_long_midpoint = val[i].longitude - temp;
                 }
               }
               var object = {
                   "cctv": val[i],
                   "cctv_id":val[i].cctv_id,
                   "prev_cctv": prev_cctv,
                   "next_cctv": next_cctv,
                   "prev_lat_midpoint": prev_lat_midpoint,
                   "prev_long_midpoint": prev_long_midpoint,
                   "next_lat_midpoint": next_lat_midpoint,
                   "next_long_midpoint": next_long_midpoint,
                   "car_count": null
               }
               list.push(object);
             }
           }
         }
         else if( key === "I-15" || key === "I-215"){
           for(i=0;i < val.length;i++){
             var prev_cctv = null;https://reactjs.org/docs/state-and-lifecycle.html
             if(i!== 0){
               prev_cctv=val[i-1];
             }
             var next_cctv = null;
             if(i !== (val.length-1)){
               next_cctv = val[i+1];
               //Calc distance
               var prev_lat_midpoint = null;
               var prev_long_midpoint = null;
               var next_lat_midpoint = null;
               var next_long_midpoint = null;
               var temp = null;
               if(prev_cctv !== null){
                 if(prev_cctv.latitude > val[i].latitude){
                     temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
                     prev_lat_midpoint = val[i].latitude + temp;
                 }
                 else{
                     temp = Math.abs(prev_cctv.latitude-val[i].latitude)/2;
                     prev_lat_midpoint = val[i].latitude - temp;
                 }
                 if(prev_cctv.longitude > val[i].longitude){
                    temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
                    prev_long_midpoint = val[i].longitude + temp;
                 }
                 else{
                    temp = Math.abs(prev_cctv.longitude-val[i].longitude)/2;
                    prev_long_midpoint = val[i].longitude - temp;
                 }
               }
               if(next_cctv !== null){
                 if(next_cctv.latitude > val[i].latitude){
                   temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
                   next_lat_midpoint = val[i].latitude + temp;
                 }
                 else{
                   temp = Math.abs(next_cctv.latitude-val[i].latitude)/2;
                   next_lat_midpoint = val[i].latitude - temp;
                 }
                 if(next_cctv.longitude > val[i].longitude){
                   temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
                   next_long_midpoint = val[i].longitude + temp;
                 }
                 else{
                   temp = Math.abs(next_cctv.longitude-val[i].longitude)/2;
                   next_long_midpoint = val[i].longitude - temp;
                 }
               }
               object = {
                   "cctv": val[i],
                   "cctv_id": val[i].cctv_id,
                   "prev_cctv": prev_cctv,
                   "next_cctv": next_cctv,
                   "prev_lat_midpoint": prev_lat_midpoint,
                   "prev_long_midpoint": prev_long_midpoint,
                   "next_lat_midpoint": next_lat_midpoint,
                   "next_long_midpoint": next_long_midpoint,
                   "car_count": null
               }
               list.push(object);
             }
           }
         }
         else{
           continue;
         }
       }
     }
       this.setState({
         cctv_objects: list,
         error: false
     });
   },
   (error) => {
       //console.log(error);
       this.setState({
         cctvs: [],
           error: true
       })
   });
 //Used for updating congestion lines, every 10 seconds
   // this.intervalID = setInterval(
   //   ()=> this.update_congestion_lines(),
   //   10000
   // );

 }

 componentWillUnmount(){
   clearInterval(this.intervalID);
 }
 update_congestion_lines(){
   //Update Congestions Lines
   console.log("Updating Congestion lines");
   var cctv_objects_dup = Array.from(this.state.cctv_objects);
   //Go through all cameras
   for(var i = 0; i < cctv_objects_dup.length;i++){
     var target_url = "http://highwayanalytics.us/api/vehicle/?cctv="+cctv_objects_dup[i].cctv_id+"&format=json";
     var target_photo_id = null;
     if(cctv_objects_dup[i].cctv_id !== undefined){
       fetch(target_url)
         .then(res => res.json())
         .then(
           (result) => {
             if(result !== undefined){
               if(result.results !== undefined){
                 if(result.results[0] !== undefined){
                   //Find most recent photo id on specific camera
                   target_photo_id = result.results[0].photo;
                   target_url = "http://highwayanalytics.us/api/vehicle/?photo="+target_photo_id+"&format=json";
                   fetch(target_url)
                   .then( res => res.json())
                   .then(
                     (result) => {
                       //assign that camera the result of photo count
                         for(let index = 0; index < cctv_objects_dup.length; index++){
                           if(cctv_objects_dup[index] === undefined){
                             console.log("undefined");
                             continue;
                           }
                           if(cctv_objects_dup[index].cctv_id === result.results[0].cctv){
                             cctv_objects_dup[index].car_count = result.count;
                             break;
                           }
                         }
                     },
                     (error) =>{
                       console.log("Error with using target_photo_id");
                     }
                   );
                 }
               }
             }
           },
           (error) => {
             console.log("Error updating Congestion");
           }
         );
     }
   }
   //update cctv objects with new car counts
   this.setState({
     cctv_objects: cctv_objects_dup
   });
   //console.log("Lines Done Updating",this.state.cctv_objects[0].car_count);
   //console.log("here is cctv_objects",this.state.cctv_objects);
 }
 onMarkerClick = (props, marker) => {
   var latest_image = "http://highwayanalytics.us/api/search/?search=" + props.name;
   var path = [];

   fetch(latest_image)
     .then(res => res.json())
     .then(
       (result) => {
         //console.log(result.results[0].file_name)
         path.push(result.results[0].file_name)

         this.setState({
           activeMarker: marker,
           selectedPlace: props,
           showingInfoWindow: true,
           image_path : path,
         });
       }
     )
 };

 onMouseoverMarker= (props, marker, e) => {
   // this.setState({
   //   activeMarker: marker,
   //   selectedPlace: props,
   //   showingInfoWindow: true
   // })
   // console.log(this.state.showingInfoWindow)
 };

 onMouseoutMarker= (props, marker, e) => {
   // this.setState({
   //   activeMarker: null,
   //   showingInfoWindow: false
   // })
   // console.log(this.state.showingInfoWindow)
 };

 onInfoWindowClose = () =>
   this.setState({
     activeMarker: null,
     showingInfoWindow: false
   });

 onMapClicked = () => {
   if (this.state.showingInfoWindow){
     this.setState({
       activeMarker: null,
       showingInfoWindow: false,
       image_path: null,
     })
     console.log(this.state.showingInfoWindow)
   }
 };

 grabColor = (car_count) =>{
   console.log("Car Count", car_count);
   if(car_count === null){
     return 'green';
   }
   else{
     return 'red';
   }
 }
 renderMap(){
   var icon_image = process.env.PUBLIC_URL + '/camera_icon2.png';

   var cctvs = this.state.cctvs.map(
     (d) =>
       <Marker
         icon={icon_image}
         name = {d.id}
         onClick = {this.onMarkerClick}
         onMouseover={this.onMouseoverMarker}
         onMouseout={this.onMouseoutMarker}
         position = { {lat: d.latitude, lng: d.longitude} }
         lat = {d.latitude}
         long = {d.longitude}
         image_url = {d.image_url}
         route = {d.route}
       />
   );
   var prev_congestion_lines = this.state.cctv_objects.map(
     (object)=>(
       //prev_polyline
           <Polyline
             key= {object.cctv.latitude.toString() + object.cctv.longitude.toString()}
             path={[
               { lat: object.prev_lat_midpoint, lng: object.prev_long_midpoint},
               { lat: object.cctv.latitude, lng: object.cctv.longitude},
             ]}
             options={{
             strokeColor: this.grabColor(object.car_count),
             strokeOpacity: 0.75,
             strokeWeight: 10,
             icons: [{
               offset: '0',
               repeat: '10px'}],
             }}
           />
     )
   );
   var next_congestion_lines = this.state.cctv_objects.map(
     (object)=>(
       //prev_polyline
           <Polyline
             key = {object.cctv_id.toString() + object.cctv.latitude.toString() + object.cctv.longitude.toString()}
             path={[
               { lat: object.cctv.latitude, lng: object.cctv.longitude},
               { lat: object.next_lat_midpoint, lng: object.next_long_midpoint},
             ]}
             options={{
             strokeColor: this.grabColor(object.car_count),
             strokeOpacity: 0.75,
             strokeWeight: 10,
             icons: [{
               offset: '0',
               repeat: '10px'}],
             }}
           />
     )
   );
   return (
     <div style={{ height: '92vh', width: '100%' }}>
       <Map
         google={this.props.google}
         zoom={this.props.zoom}
         style={this.props.style}
         initialCenter={this.props.center}
         onClick={this.onMapClicked}
       >
         {cctvs}
         {prev_congestion_lines}
         {next_congestion_lines}
       </Map>
     </div>
   );
 }

 renderTable(){
   var content = []

   var path = "http://highwayanalytics.us/image/" + this.state.image_path;

   if (this.state.showingInfoWindow !== false) {
     content.push(
       <div className="row" style={{'padding': '35px'}}>
         <h2> Marker Information</h2>
         <img
           src = {path}
           style={{
             width: '380px'
           }}
         />
         <div>
           <span> Lat : {this.state.selectedPlace.lat} Long: {this.state.selectedPlace.long}</span>
           <p> Route : {this.state.selectedPlace.route} Marker_Id : {this.state.selectedPlace.name}</p>
         </div>
       </div>
     );
   }else{
     content.push(
       <div className="row" style={{'padding': '35px'}}>
         <h2> Marker Information</h2>
       </div>
     );
   }
   return content;
 }

 handleClick(){
   this.update_congestion_lines();
   console.log(this.state.cctv_objects);
   this.forceUpdate();
 }
 render(){
   var map = this.renderMap();
   var table = this.renderTable();
   return(
     <div className="row">
       <div className="col-9">
         {map}
       </div>
       <div className="col-3">
         {table}
         <button onClick={this.handleClick}> Update Congestion Lines</button>
       </div>
     </div>
   );
 }
}

export default GoogleApiWrapper({
 apiKey: process.env.REACT_APP_GOOGLE_MAP_KEY
})(HomepageMap);

renderMap() se encarga de generar el mapa, los marcadores y las polilíneas. El color de la polilínea debe cambiar según car_count. Como puede ver, estoy registrando el conteo de autos, el conteo de autos debe ser nulo inicialmente pero no es nulo después de que se establece el estado, aunque console.log me dice que car_count no es nulo, el color de la línea no cambia.

 grabColor = (car_count) =>{
    console.log("Car Count", car_count);
    if(car_count === null){
      return 'green';
    }
    else{
      return 'red';
    }
  }
  renderMap(){
    var icon_image = process.env.PUBLIC_URL + '/camera_icon2.png';

    var cctvs = this.state.cctvs.map(
      (d) =>
        <Marker
          icon={icon_image}
          name = {d.id}
          onClick = {this.onMarkerClick}
          onMouseover={this.onMouseoverMarker}
          onMouseout={this.onMouseoutMarker}
          position = { {lat: d.latitude, lng: d.longitude} }
          lat = {d.latitude}
          long = {d.longitude}
          image_url = {d.image_url}
          route = {d.route}
        />
    );
    var prev_congestion_lines = this.state.cctv_objects.map(
      (object)=>(
        //prev_polyline
            <Polyline
              key= {object.cctv.latitude.toString() + object.cctv.longitude.toString()}
              path={[
                { lat: object.prev_lat_midpoint, lng: object.prev_long_midpoint},
                { lat: object.cctv.latitude, lng: object.cctv.longitude},
              ]}
              options={{
              strokeColor: this.grabColor(object.car_count),
              strokeOpacity: 0.75,
              strokeWeight: 10,
              icons: [{
                offset: '0',
                repeat: '10px'}],
              }}
            />
      )
    );
    var next_congestion_lines = this.state.cctv_objects.map(
      (object)=>(
            <Polyline
              key = {object.cctv_id.toString() + object.cctv.latitude.toString() + object.cctv.longitude.toString()}
              path={[
                { lat: object.cctv.latitude, lng: object.cctv.longitude},
                { lat: object.next_lat_midpoint, lng: object.next_long_midpoint},
              ]}
              options={{
              strokeColor: this.grabColor(object.car_count),
              strokeOpacity: 0.75,
              strokeWeight: 10,
              icons: [{
                offset: '0',
                repeat: '10px'}],
              }}
            />
      )
    );

Esto es lo que obtengo en la consola y cómo se ve el mapa.

Si alguien tiene una explicación de por qué las polilíneas no se vuelven a representar después de los cambios en car_count, estaría muy agradecido.

Editar: agregué el resto de la función del primer bloque de código y agregué una clave a la línea next_congestion como se indica en los comentarios.

  • Fromm qué biblioteca hace el Polyline componente viene? ¿Puedes crear un ejemplo en vivo que podamos probar?

    – XCS

    17 de marzo de 2020 a las 1:39


  • Te perdiste parte de tu código (la parte que devuelve algo de la función). También creo que es más limpio (y potencialmente más eficaz) dividir el mapa y la tabla en componentes separados

    – Domingo

    17 de marzo de 2020 a las 1:40

  • También tenga en cuenta que su next_congestion_lines matriz no tiene keys para cada Polyline.

    – XCS

    17 de marzo de 2020 a las 1:41

  • Ustedes decir está llamando a setState, pero no nos muestra dónde ni cómo.

    – Jared Smith

    17 de marzo de 2020 a las 1:55

  • He agregado todo el archivo para mostrar dónde estoy configurando el estado, lo estoy configurando en la función update_congestion_lines. Perdón.

    – Winson Bi

    17 de marzo de 2020 a las 2:02

¿Ha sido útil esta solución?