当前位置: 首页 > 知识库问答 >
问题:

React 组件在第一次按钮单击后不更新

边明煦
2023-03-14

我的代码生成一个输入字段,允许用户输入要搜索的值。然后当他们单击提交按钮时,它会使displayMap为true,因此当MapDisplay组件呈现时,它将通过Map组件触发API搜索并返回随后显示在地图上的值。

问题是这个过程只工作一次。当我再次单击该按钮时,它确实做了一些事情,我确认它正在输入框中获取新值,但我似乎不知道如何再次渲染地图。

我尝试在this.setState中设置其他变量,试图让它知道它需要再次渲染组件,但我想我错过了一些东西,因为什么都不起作用。

我对反应相当陌生,所以你能提供的任何帮助都将不胜感激。

这是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>
         )
    }

共有2个答案

东门焕
2023-03-14

@wxker 感谢您的帮助!你肯定让我朝着正确的方向前进。

我在MainSearchBar中更改了渲染。js回到了最初的状态。

我在地图组件中添加了一个ComponentDidUpdate,如下所示:

async componentDidUpdate(prevProps) {

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

然后其余的和原来的组件一样。

章松
2023-03-14

再次单击该按钮时,主搜索栏的状态.js更新,但功能组件 MapDisplay 不会更新,因此地图也不会更新。

有许多方法可以解决这个问题。看一下代码,看起来MapDisplay做的不多,所以你可以考虑用条件渲染来代替它。

主搜索栏.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 = {props.searchTerm}></Map>}
            </div>
         )
    }

然后在您的Map组件中,添加一个组件迪迪更新生命周期方法来检测道具的更新,该方法在道具更新时执行与组件迪迪安装相同的操作。

  async componentDidMount(prevProps) {
    if (props.toSearch != prevProps.toSearch) {
      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
    }
  }    
 类似资料:
  • 在我点击提交按钮后,我如何使页面刷新或更新页面中的内容?我尝试将(我知道不是React方式,具有相同的结果)放入提交函数(、)中,但POST请求没有得到响应 OpenTickets.js CardConversation.jsx

  • 我正在尝试在React中构建我的第一个计算器。我的纽扣似乎有问题(我想)。在测试s时,我发现我第一次点击添加按钮不会更新所有setState。相反,第二次单击会正确更新它。 知道它为什么不起作用吗? 我有一个名为“DaInserting”的div,当用户点击任何数字键时会更新,然后当用户点击加法/减法/等时,应该更新,但它没有。它在第二次点击时更新:-检查。 当用户点击加法/减法后,DaInser

  • 应用: 带有两个文本输入字段(input1、input2)的搜索栏 三个按钮:SearchX、SearchY、Clear Results 两个搜索都可以将input1和input2作为参数,以得到两个不同的结果。 有一个结果组件,它接受输入、操作,并根据操作呈现搜索组件。 问题: 我们希望只有在单击按钮时才启动搜索。使用下面的代码,在第一个搜索结果之后,只要您更改输入,结果组件就会预期地重新呈现,

  • 问题内容: 我正在尝试用JavaFX创建我的第一个应用程序,而Button调用一个方法时遇到了问题(例如,打开另一个窗口)-我总是必须单击两次才能触发操作。 这是来自控制器的代码: 我的方法在这里实现: 问题答案: 最有可能在FXML中包含以下内容: 这使您的按钮可以调用您的方法。但是,您没有定义要在单击按钮时执行的逻辑,而是第一次说:“单击此按钮时,在我的按钮上放一个会叫“ 因此,您的第一次单击

  • 问题内容: 以下是HTML。 以下是Javscript 还有一些CSS 我要的是单击适当的按钮后,将Board组件的大小调整为适当的大小。 我已经尝试过这样 但这是行不通的。 问题答案: 您不能像这样设置React组件的状态。并且组件应负责设置自己的状态。 在您的Board组件内部,在componentDidMount中设置事件侦听器。最好的解决方案是让按钮成为React应用程序的一部分,但这超出

  • 问题内容: 单击按钮如何只打印一个组件。 我知道这个解决方案: 但是React不想使用框架。 有什么办法吗?谢谢。 问题答案: 客户端上有两种解决方案。一种是像您张贴的框架。不过,您可以使用iframe: 这期望这个html存在 另一种解决方案是使用媒体选择器,并在样式上隐藏所有您不想打印的内容。 最后一种方法需要在服务器上进行一些工作。您可以将所有HTML + CSS发送到服务器,并使用许多组件