Skip to content

Instantly share code, notes, and snippets.

@pi3r0
Last active September 2, 2019 12:27
Show Gist options
  • Save pi3r0/f08a7e8ab63ba193a6abc26dffa86c5d to your computer and use it in GitHub Desktop.
Save pi3r0/f08a7e8ab63ba193a6abc26dffa86c5d to your computer and use it in GitHub Desktop.
Refactoring work - this a react component, working independently, this show weather forecast, at the refresh from the user location if user allowed this and via a search bar (inside the component) where you write a city name, then it fetches the result. Don't bother with import classes, this is not the problem here, you can add one if you have t…
import React from "react";
import Form from "./Form";
import WeatherDiv from "./WeatherDiv";
import "./Form.css"
import "./WeatherAPi.css"
import Slider from "react-slick"
import ActivityCards from "./ActivityCards"
import Activity from "./Activity.json"
import { Link } from 'react-router-dom'
class WeatherAPI extends React.Component {
state = {
city: '',
//just a UI state to show or hide loading div, and i think that the bug comes from here
loading: true,
erreur:'',
weatherData: []
}
//Called when the user write city into the searching bar
getWeather = (e) => {
// In any case prevent default (safety) if will stop another request already launched
e.preventDefault();
const city = e.target.city.value;
this.retrieveWeather("q=" + city)
}
//fonction qui permet d'obtenir la localisation
getLocation = () => {
//si l'utilisateur accepte d'être géolocalisé
if (navigator.geolocation) {
//getCurrentPosition permet de retourner un objet position qui donne notamment les coordonnées
navigator.geolocation.getCurrentPosition(position => {
// Get the weather with user lat and long
this.retrieveWeather("lat="+ position.coords.latitude + "&lon=" + position.coords.longitude)
})
} else {
//alerte qui s'active si l'utilisateur n'accepte pas d'être géolocalisé
}
}
retrieveWeather = (fetchParams) => {
//Set loading to true, so the UI with display loading screen, set to false after the result
this.setState({ loading : true }
//Your service is the same call with each exept for the params (q=${city} or lat=${this.state.lat}&lon=${this.state.lon})
fetch(`http://api.openweathermap.org/data/2.5/forecast?`+ ${fetchParams} + `&lang=fr&units=metric&appid=e8fc88dd5f1edd7f7ff6a9a5be06bd83`)
.then(res => res.json())
.then(res => this.setState({
weatherData: res,
city: res.city.name,
loading: false,
erreur:'pfffff'}))
}
}
//This method is called once at the page creation, you have not to worry to test if it's geoloc or city search
componentDidMount() {
//for the first time, check user location and then retrieve the weather
this.getLocation()
}
render() {
const param = {
dots: true,
infinite: true,
speed: 1500,
slidesToShow: 3,
slidesToScroll: 3,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: false,
}
},
{
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 0,
infinite: true
}
},
{
breakpoint: 480,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
infinite: true,
repeat: 99
}
}
]
}
const settings = {
dots: false,
infinite: false,
speed: 800,
slidesToShow: 1,
slidesToScroll: 1
};
//le if/else, permet de s'assurer que le render ne s'active que quand l'API a bien chargé ses données dans le state et donc transformé loading en false
if (this.state.loading) {
return (<div>loading</div>)
} else if (this.state.weatherData.list[0].weather[0].main === "Rain") {
return (
<div>
<div>
<Form getWeather={this.getWeather} city={this.state.city} />
</div>
<div>
<Slider {...settings} >
{/*le filter map permet de ne sélectionner que une prévision par jour dans le tableau d'objets list contenu dans l'objet weatherData. Autrement list contient des prévisions toutes les trois heures. */}
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={this.state.city} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.filter(data => data.type.toString().includes("beau"))
.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}> <ActivityCards {...data} /></Link>
</div>
)
})}
</Slider>
</div>
)
} else {
return (
<div>
<div>
<Form getWeather={this.getWeather} city={this.state.city} />
</div>
<div>
<Slider {...settings} >
{/*le filter map permet de ne sélectionner que une prévision par jour dans le tableau d'objets list contenu dans l'objet weatherData. Autrement list contient des prévisions toutes les trois heures. */}
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={this.state.city} erreur={this.state.erreur} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}>
<ActivityCards {...data} />
</Link>
</div>
)
})}
</Slider>
</div>
)
}
}
}
import React from "react";
import Form from "./Form";
import WeatherDiv from "./WeatherDiv";
import "./Form.css"
import "./WeatherAPi.css"
import Slider from "react-slick"
import ActivityCards from "./ActivityCards"
import Activity from "./Activity.json"
import { Link } from 'react-router-dom'
class WeatherAPI extends React.Component {
state = {
city: '',
lat: '',
loading: true,
lon: '',
erreur:'',
weatherData: []
}
getWeather = (e) => {
if (this.state.loading) {
fetch(`http://api.openweathermap.org/data/2.5/forecast?lat=${this.state.lat}&lon=${this.state.lon}&lang=fr&units=metric&appid=e8fc88dd5f1edd7f7ff6a9a5be06bd83`)
.then(res => res.json())
.then(res => this.setState({ weatherData: res, loading: false, city: res.city.name }))
} else {
e.preventDefault();
const city = e.target.city.value;
fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&lang=fr&units=metric&appid=e8fc88dd5f1edd7f7ff6a9a5be06bd83`)
.then(res => res.json())
.then(res => this.setState({
weatherData: res,
city: res.city.name ,
erreur:'pfffff'}))
}
}
getLocation = () => {
// try if user gives us the geoloc
if (navigator.geolocation) {
//Access to user position
navigator.geolocation.getCurrentPosition(position => {
this.setState({ lon: position.coords.longitude, lat: position.coords.latitude })
this.getWeather()
})
} else {
//DO nothing if the user did not active the location
}
}
componentDidMount() {
this.getLocation()
}
render() {
const param = {
dots: true,
infinite: true,
speed: 1500,
slidesToShow: 3,
slidesToScroll: 3,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: false,
}
},
{
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 0,
infinite: true
}
},
{
breakpoint: 480,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
infinite: true,
repeat: 99
}
}
]
}
const settings = {
dots: false,
infinite: false,
speed: 800,
slidesToShow: 1,
slidesToScroll: 1
};
if (this.state.loading) {
return (<div>loading</div>)
} else if (this.state.weatherData.list[0].weather[0].main === "Rain") {
return (
<div>
<div>
<Form getWeather={this.getWeather} city={this.state.city} />
</div>
<div>
<Slider {...settings} >
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={this.state.city} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.filter(data => data.type.toString().includes("beau"))
.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}> <ActivityCards {...data} /></Link>
</div>
)
})}
</Slider>
</div>
)
} else {
return (
<div>
<div>
<Form getWeather={this.getWeather} city={this.state.city} />
</div>
<div>
<Slider {...settings}
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={this.state.city} erreur={this.state.erreur} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}>
<ActivityCards {...data} />
</Link>
</div>
)
})}
</Slider>
</div>
)
}
}
}
// Refactoring work - this a react component, working independently, this show weather forecast, at the refresh from the user location if user allowed this and via a search bar (inside the component) where you write a city name, then it fetches the result. Don't bother with import classes, this is not the problem here, you can add one if you have to. The goal is to refactor the functions (getWeather / getLocation) and inside the render() function. You can change state var or add one. things must be write once if it's possible and your code must be clear and understandable.
import React from "react";
import Form from "./Form";
import WeatherDiv from "./WeatherDiv";
import "./Form.css"
import "./WeatherAPi.css"
import Slider from "react-slick"
import ActivityCards from "./ActivityCards"
import Activity from "./Activity.json"
import { Link } from 'react-router-dom'
class WeatherAPI extends React.Component {
state = {
city: '',
lat:'',
loading: true,
lon:'',
erreur:'',
weatherData: []
}
// First, verify geolocalistion and update state for longitude + latitude
getLocation = () => {
// try if user gives us the geoloc
if (navigator.geolocation) {
//Access to user position with a closure so it's async.
navigator.geolocation.getCurrentPosition(position => {
this.setState({ lon: position.coords.longitude, lat: position.coords.latitude })
//You had to call getWeather right there.
})
} else {
alert (`You're not geolocated.`)
}
}
// Second, put city state with value in form, and create constante with state lat + lon after getLocation executed, and execute getWeather
getCity = (e) => {
e.preventDefault();
let city = e.target.city.value;
const lat = this.state.lat;
const lon = this.state.lon;
//You passed params on getWeather but your function didn't had any
this.getWeather(city, lat, lon);
}
//Third, verify state loading and fetch weather with params
// this.getWeather(city, lat, lon) but there is no inputs, if you want to do this see below
// getWeather = (cityName, latitude, longitude) => {
getWeather = () => {
//state.loading is a UI state, you can chose an other way to check if it's from geoloc or search, with parameters for exemple, also if loading is UI state, you can set to true every time
// you relaod the search to avoiding bad UI Refresh if the connection is slow.
// You can test if city is empty or like in my exemple by replacing params into the URL.
// The URL is the same, there are One change to make and you can use it for both cases.
if (this.state.loading){
fetch(`http://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&lang=fr&units=metric&appid=e8fc88dd5f1edd7f7ff6a9a5be06bd83`)
.then(res => res.json())
.then(res => this.setState({ weatherData: res, loading: false, city: res.city.name }))
} else {
// if client it's not geolocalised
fetch(`http://api.openweathermap.org/data/2.5/forecast?q=${city}&lang=fr&units=metric&appid=e8fc88dd5f1edd7f7ff6a9a5be06bd83`)
.then(res => res.json())
.then(res => this.setState({ weatherData: res, city: res.city.name }))
}
}
componentDidMount() {
//Warning this function is async
this.getLocation()
//You can't call getCity right after getLocation, the state will be empty (if you are lucky no) but use .then if you want to
//Even if getLocation was sync, why you wanted to call getCity, call getWeather()
this.getCity()
}
render() {
const {
city,
erreur,
} = this.state
const param = {
dots: true,
infinite: true,
speed: 1500,
slidesToShow: 3,
slidesToScroll: 3,
initialSlide: 0,
responsive: [
{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: false,
}
},
{
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
initialSlide: 0,
infinite: true
}
},
{
breakpoint: 480,
settings: {
slidesToShow: 2,
slidesToScroll: 2,
infinite: true,
repeat: 99
}
}
]
}
const settings = {
dots: false,
infinite: false,
speed: 800,
slidesToShow: 1,
slidesToScroll: 1
};
//there is no error test, and you have the same code here twice.
if (this.state.loading) {
return (<div>loading</div>)
//It's to dangerous to get an element at index, you have to test if it exists first and after access to the value.
} else if (this.state.weatherData.list[0].weather[0].main === "Rain") {
return (
<div>
<div>
<Form getWeather={this.getCity} city={city} />
</div>
<div>
<Slider {...settings} >
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={city} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.filter(data => data.type.toString().includes("Sunshine"))
.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}> <ActivityCards {...data} /></Link>
</div>
)
})}
</Slider>
</div>
)
} else {
return (
<div>
<div>
<Form getWeather={this.getCity} city={city} />
</div>
<div>
<Slider {...settings} >
{this.state.weatherData.list
.filter(data => data.dt_txt.includes("12:00:00"))
.map((data, index) => (
<WeatherDiv {...data} city={city} erreur={erreur} index={index} key={index} />
))}
</Slider>
</div>
<Slider className="sliderActivity"{...param}>
{Activity.map((data) => {
let url = `/activity/${data.id}`
return (
<div key={data.id}>
<Link to={url}>
<ActivityCards {...data} />
</Link>
</div>
)
})}
</Slider>
</div>
)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment