4

私のコードは、ユーザーが検索する値を入力できる入力フィールドを生成します。次に [送信] ボタンをクリックすると、displayMap が true になり、MapDisplay コンポーネントがレンダリングされると、Map コンポーネントを介して API 検索がトリガーされ、マップに表示される値が返されます。

問題は、このプロセスが一度しか機能しないことです。ボタンをもう一度クリックすると、何かが行われ、入力ボックスに新しい値が取得されていることを確認しましたが、マップを再度レンダリングする方法がわかりません。

this.setState に他の変数を設定して、コンポーネントを再度レンダリングする必要があることを認識させようとしましたが、何も機能しないため、何かが欠けていると思います。

私はReactにかなり慣れていないので、あなたが提供できる助けがあれば大歓迎です。

これは MainSearchBar.js で、上記のほとんどの作業が行われています。

import Map from './newMap.js';

function MapDisplay(props) {
  if (props.displayMap) {
    return <Map toSearch = {props.searchTerm}></Map>;
  } else {
    return "";
  }
}

class MainSearchBar extends React.Component {

    constructor(props) {
      super(props);
      this.state = {
        displayMap: false,
        value: '',
        searchTerm: '',
        isOpened: false
      };
      //this.handleClick = this.handleClick.bind(this);
      this.handleChange = this.handleChange.bind(this);
    }

    handleClick = () => {
        this.setState({

          displayMap: true,
          isOpened: !this.state.isOpened,
          searchTerm: this.state.value
          });
        console.log(this.state.value);
      }

    handleChange(event) {
      this.setState({value: event.target.value});

    }

    render() {
      const displayMap = this.state.displayMap;
         return (
            <div class="homepage-search-bar">
              <input 
                type="text" name="search" value={this.state.value} onChange={this.handleChange} className="main-search-bar" placeholder="Search hashtags">
              </input>
              <button onClick={this.handleClick}>Search</button>
              <MapDisplay displayMap={displayMap} searchTerm={this.state.value} />  
            </div>
         )
    }
}

export default MainSearchBar;

これは MainSearchBar が呼び出されている場所です

import Top20Box from '../components/getTop20Comp2.js';
import Header from '../components/Header.js';
import MainIntro from '../components/MainIntro.js';
import MainSearchBar from '../components/MainSearchBar.js';
import MainCTA from '../components/MainCTA.js';
import Footer from '../components/Footer.js';

export default class Home extends Component { 
  state = { 
  }

  render () {                                   
      return (
        <React.Fragment>
              <Header>
              </Header>
              <MainIntro />
              <MainSearchBar />
              <div className="top20-text">
                Top 20 trending hashtags
              </div>
              <Top20Box />
              <MainCTA />
              <Footer />
         </React.Fragment>
      )
   }
}

必要な場合に備えて、これは Map コンポーネント自体です。

import React from 'react';
import ReactMapGL, {Marker, Popup} from 'react-map-gl';
import axios from 'axios';

//for the loading animation function
import FadeIn from "react-fade-in";
import Lottie from "react-lottie";
import * as loadingData from "../assets/loading.json";

var locationCoordinates = [];
var locationToSearch = "";
var returnedKeywordSearch = [];
var newArray = [];

const defaultOptions = {
  loop: true,
  autoplay: true,
  animationData: loadingData.default,
  rendererSettings: {
    preserveAspectRatio: "xMidYMid slice"
  }
};

export default class Map extends React.Component {

//sets components for the map, how big the box is and where the map is centered when it opens
  state = {
            viewport: {
              width: "75vw",
              height: "50vh",
              latitude: 40.4168,
              longitude: 3.7038,
              zoom: .5
            },
            tweetSpots: null, //data from the API
            selectedSpot: null,
            done: undefined, //for loading function
          };

  async componentDidMount() {
    //searches the api for the hashtag that the user entered

    await axios.get(`https://laffy.herokuapp.com/search/${this.props.toSearch}`).then(function(response) {
        returnedKeywordSearch = response.data;
      }) //if the api call returns an error, ignore it
      .catch(function(err) {
        return null;
      });

      //goes through the list of locations sent from the api above and finds the latitude/longitude for each
      var count = 0;
      while (count < returnedKeywordSearch.length) {
        locationToSearch = returnedKeywordSearch[count].location;
        if (locationToSearch !== undefined) {
          var locationList = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/${locationToSearch}.json?access_token=pk.eyJ1IjoibGF1bmRyeXNuYWlsIiwiYSI6ImNrODlhem95aDAzNGkzZmw5Z2lhcjIxY2UifQ.Aw4J8uxMSY2h4K9qVJp4lg`)
          .catch(function(err) {
            return null;
          });

          if (locationList !== null) {
            if (Array.isArray(locationList.data.features) && locationList.data.features.length)  
             {
              locationCoordinates.push(locationList.data.features[0].center);
              if (returnedKeywordSearch[count].location!== null && returnedKeywordSearch[count].location!==""
                  && locationList.data.features[0].center !== undefined)
                {newArray.push({
                            id: returnedKeywordSearch[count].id, 
                            createdAt: returnedKeywordSearch[count].createdAt,
                            text: returnedKeywordSearch[count].text,
                            name: returnedKeywordSearch[count].name,
                            location: returnedKeywordSearch[count].location,
                            coordinates: locationList.data.features[0].center
                });
                }
            } 
          }
        }

        count++;
      }
      this.setState({tweetSpots: newArray});
      this.setState({ done: true}); //sets done to true so that loading animation goes away and map displays
  }     
//is triggered when a marker on the map is hovered over
  setSelectedSpot = object => {
    this.setState({
     selectedSpot: object
    });
  };

//creates markers that display on the map, using location latitude and longitude
  loadMarkers = () => {
    return this.state.tweetSpots.map((item,index) => {
      return (
        <Marker
          key={index}
          latitude={item.coordinates[1]}
          longitude={item.coordinates[0]}
        >
          <img class="mapMarker"
            onMouseOver={() => {
              this.setSelectedSpot(item);
            }}
            src="/images/yellow7_dot.png" alt="" />
        </Marker>
      );
    });
  };

//closes popup when close is clicked
  closePopup = () => {
    this.setState({
      selectedSpot: null
    }); 
  };

 //renders map component and loading animation
  render() {
    return (
      <div className="App">
        <div className="map">
          {!this.state.done ? (
          <FadeIn>
            <div class="d-flex justify-content-center align-items-center">
              <Lottie options={defaultOptions} width={400} />
            </div>
          </FadeIn>
        ) : (
        <ReactMapGL  {...this.state.viewport} mapStyle="mapbox://styles/mapbox/outdoors-v11"
         onViewportChange={(viewport => this.setState({viewport}))} 
         mapboxApiAccessToken="pk.eyJ1IjoibGF1bmRyeXNuYWlsIiwiYSI6ImNrODlhem95aDAzNGkzZmw5Z2lhcjIxY2UifQ.Aw4J8uxMSY2h4K9qVJp4lg">

          {this.loadMarkers()}

          {this.state.selectedSpot !== null ? (
            <Popup
              key={this.state.selectedSpot.id}
              tipSize={5}
              latitude={this.state.selectedSpot.coordinates[1]}
              longitude={this.state.selectedSpot.coordinates[0]}
              closeButton={true}
              closeOnClick={false}
              onClose={this.closePopup}
            >
               <div className="mapPopup">
                 <div className="header"> Tweet </div>
                 <div className="content">
                   {" "}
                 <p>
                   <b>Name:</b> {this.state.selectedSpot.name}
                 </p>
                 <p>
                   <b>Tweet:</b> {this.state.selectedSpot.text}</p>
                   <p><a href={'https://www.twitter.com/user/status/' + this.state.selectedSpot.id}target="_blank" rel="noopener noreferrer">View Tweet in Twitter</a>
                  </p>
                 </div>

               </div>  
            </Popup>
          ) : null}

        </ReactMapGL>

        )}
        </div>
      </div>

      );
  }
}

更新: 4/28、受け取った回答に従って、MainSearchBar.js のレンダリングを次のように更新します。

render() {
      const displayMap = this.state.displayMap;
         return (
            <div class="homepage-search-bar">
              <input 
                type="text" name="search" value={this.state.value} onChange={this.handleChange} className="main-search-bar" placeholder="Search hashtags">
              </input>
              <button onClick={this.handleClick}>Search</button>

              {this.state.displayMap && <Map toSearch = {this.searchTerm}></Map>}


            </div>
         )
    }
4

2 に答える 2

0

@wxker ご協力ありがとうございます。あなたは確かに私を正しい方向に向けさせました。

MainSearchBar.js の render を元に戻しました。

そして、以下のように、Map コンポーネントに ComponentDidUpdate を追加しました。

async componentDidUpdate(prevProps) {

    //searches the api for the hashtag that the user entered
    if (this.props.toSearch !== prevProps.toSearch) {

残りは元のcomponentDidMountと同じでした。

于 2020-04-29T23:15:44.707 に答える