我的代码生成一个输入字段,允许用户输入要搜索的值。然后当他们单击提交按钮时,它会使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>
)
}
@wxker 感谢您的帮助!你肯定让我朝着正确的方向前进。
我在MainSearchBar中更改了渲染。js回到了最初的状态。
我在地图组件中添加了一个ComponentDidUpdate,如下所示:
async componentDidUpdate(prevProps) {
//searches the api for the hashtag that the user entered
if (this.props.toSearch !== prevProps.toSearch) {
然后其余的和原来的组件一样。
再次单击该按钮时,主搜索栏的状态.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发送到服务器,并使用许多组件