问题
Edit: I think this is a CORS issue with codesandbox, but I haven't figured out how to host with static ip or even localhost yet. I'll update when I can confirm it is a CORS issue between papaparse and codesandbox cloud ide. I started a new question for a different problem than Domino solved here on the same papaparse application
This question here on StackOverflow is an extension of my conversation with Papa.parse.
A pattern is found here, similarly I use a this.function to pass down every object as it is parsed with "City" parameter & try to render a "WeatherCitySkyMap" component for each City. The code after render has been tested to render such a component for each City in the cities array if I hard code let cities = [{ city: "New York" }, { city: "Baltimore" }];, but I want cities to be populated by Papa.parse step for each City parsed from my dropboxlink of a csv list of cities in NorthAmerica.
class CitiesMap extends React.Component {
constructor(props) {
super(props);
this.updateData = this.updateData.bind(this);
}
componentDidMount() {
Papa.parse(
"https://cors-anywhere.herokuapp.com/https://www.dropbox.com/s/5vcoo9r60hgczd1/worldcitiespop_northamerica_nolonglat.csv?dl=1",
{
download: true,
header: true,
worker: true,
skipEmptyLines: true,
step: this.updateData,
complete: function() {
console.log("all done");
}
}
);
}
updateData(results) {
cities.push(results.data, ["City"]);
this.setState({ cities });
}
render(props) {
let filteredCities = cities.filter(cities => {
return (
cities.City.toUpperCase().indexOf(this.props.search.toUpperCase()) !==
-1
);
});
return (
<div>
<div className="Cities">
{filteredCities.map(City => {
return (
<div>
<WeatherCitySkyMap
description={this.description}
humidity={this.humidity}
/>
{JSON.stringify([City])
.replace(/[^\w\s]/g, "")
.replace(/(city)/g, "")}
</div>
);
})}
</div>
</div>
);
}
}
export default CitiesMap;
"This pattern is found here" link uses UNSAFE_componentWillMount()), instead I try to use componentDidMount() around Papa.parse, but need the Papa.parse results.data to happen before they render like componentWillMount() would have done (or should I use shouldComponentUpdate & forceUpdate() somewhere?). I read best practices is to replace componentWillMount with constructor and super, so should I put Papa.Parse in the constructor? I tried that and got lost. Thanks for reading & your help in advance.
UPDATE after Domino987 answer (so far):
import React from "react";
import WeatherCitySkyMap from "./WeatherCitySkyMap";
import Papa from "papaparse";
import ".././Explore/Cities.css";
class CitiesMap extends React.Component {
constructor(props) {
super(props);
this.updateData = this.updateData.bind(this);
this.state = { cities: [] };
}
componentDidMount() {
Papa.parse( "https://dl.dropboxusercontent.com/s/k81s5enbamijuke/worldcitiespop_northamerica_nolonglat_few.csv?dl=0",
{
download: true,
header: true,
worker: true,
skipEmptyLines: true,
step: this.updateData,
complete: function() {
console.log("all done");
}
}
);
}
updateData(results) {
this.setState(prevState => ({
cities: [...prevState.cities, results.data.City]
}));
}
render(props) {
let filteredCities = this.state.cities.filter(cities => {
return (
cities.toUpperCase().indexOf(this.props.search.toUpperCase()) !==
-1
);
});
return (
<div>
<div className="Cities">
{filteredCities.map(City => {
return (
<div>
<WeatherCitySkyMap
description={this.description}
humidity={this.humidity}
/>
{JSON.stringify([City])
.replace(/[^\w\s]/g, "")
.replace(/(city)/g, "")}
</div>
);
})}
</div>
</div>
);
}
}
export default CitiesMap;
回答1:
So there are a few misconceptions of how the component works:
- Props are not passed to render: You can access them with
this.props
. - Local state is null at beginning: in your constructor write
this.state = {cities: []}
, This will prevent breaking your code if you try to access cities within render. - To access cities in your render and updateData use
this.state.cities
. This will let you access the current cities. - To update an array within the state write it like this:
this.setState(prevState => { cities: [...prevState.cities, results.data.City] });
. This will update the state immutable and will keep the previous cities.
componentDidMount is the correct way to implement a server call, side effect or Papa.parse. It will render the component without data first, but as soon as data is present, it will update it. The component will not break, because with point 2 the state will not be null and the render will still work.
I hope these remarks will help you get your code working.
// Edit Since you ask how to implement updateData and render, here is some code:
updateData(results) {
// This will not work because cities is not defined here (us this.state.cities) and you mutate the data with push
// cities.push(results.data.city);
// I am not sure why you add "City" but that is in your code
this.setState(prevState => { cities: [...prevState.cities, results.data.City]});
}
render(props) {
// Filter will check each city in the cities array, so you have to write it like this:
const filteredCities = this.state.cities.filter(city => city.indexOf(this.props.search.toUpperCase()) !== -1);
}
Happy coding.
来源:https://stackoverflow.com/questions/57563114/papa-parse-componentdidmount-shouldcomponentupdate-confusion