function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function debounced(delay, fn) {
let timerId;
return function(...args) {
if (timerId) {
clearTimeout(timerId);
}
timerId = setTimeout(() => {
fn(...args);
timerId = null;
}, delay);
};
}
class CoordinatesForm extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<form>
<LatField
updateLat={newLat => {
this.props.updateCoords({ lat: newLat });
}}
lat={this.props.lat}
/>
<LongField
updateLong={newLong => {
this.props.updateCoords({ long: newLong });
}}
long={this.props.long}
/>
</form>
);
}
}
class LatField extends React.Component {
constructor(props) {
super(props);
this.state = {
lat: this.props.lat
};
}
componentDidUpdate(prevProps) {
const lat = this.props.lat;
if (lat !== prevProps.lat) {
this.setState({ lat });
}
}
updateLat = event => {
let newLat = event.target.value;
if (!newLat) {
this.setState({ lat: 0 });
return this.props.updateLat(0);
}
if (isNumeric(newLat)) {
newLat = parseFloat(newLat);
} else if (newLat !== "-") {
return;
}
this.setState({ lat: newLat });
this.props.updateLat(newLat);
};
render() {
return (
<label for="">
lat:
<input
type="text"
id="lat"
value={this.state.lat}
onChange={this.updateLat}
/>
</label>
);
}
}
class LongField extends React.Component {
constructor(props) {
super(props);
this.state = {
long: this.props.long
};
}
componentDidUpdate(prevProps) {
const long = this.props.long;
if (long !== prevProps.long) {
this.setState({ long });
}
}
updateLong = event => {
let newLong = event.target.value;
if (!newLong) {
this.setState({ long: 0 });
return this.props.updateLong(0);
}
if (isNumeric(newLong)) {
newLong = parseFloat(newLong);
} else if (newLong !== "-") {
return;
}
this.setState({ long: newLong });
this.props.updateLong(newLong);
};
render() {
return (
<label for="">
long:
<input
type="text"
id="long"
value={this.state.long}
onChange={this.updateLong}
/>
</label>
);
}
}
class TimesDisplay extends React.Component {
constructor(props) {
super(props);
this.state = {
sunrise: "Unknown",
sunset: "Unknown"
};
}
componentDidUpdate(prevProps) {
const lat = this.props.lat;
const long = this.props.long;
if (lat !== prevProps.lat || long !== prevProps.long) {
this.updateTimesDebounced(lat, long);
}
}
updateTimes = (lat, long) => {
if (isNumeric(lat) && isNumeric(long)) {
return fetch(`https://api.sunrise-sunset.org/json?lat=${lat}&lng=${long}`)
.then(response => {
if (!response.ok) {
this.setState({
sunrise: "Invalid",
sunset: "Invalid"
});
throw Error(`${response.status} response on times request`);
}
return response.json();
})
.then(data => {
this.setState({
sunrise: data.results.sunrise,
sunset: data.results.sunset
});
})
.catch(error => {
console.error(error.message);
});
}
}
updateTimesDebounced = debounced(500, this.updateTimes)
render() {
return (
<div>
<SunriseTime time={this.state.sunrise} />
<SunsetTime time={this.state.sunset} />
</div>
);
}
}
class SunriseTime extends React.Component {
render() {
return <div class="sunrise">Sunrise: {this.props.time}</div>;
}
}
class SunsetTime extends React.Component {
render() {
return <div class="sunset">Sunset: {this.props.time}</div>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
lat: 0,
long: 0
};
}
updateCoords = updateObject => {
this.setState(updateObject);
};
componentDidMount() {
navigator.geolocation.getCurrentPosition(position => {
const lat = position.coords.latitude;
const long = position.coords.longitude;
this.setState({ lat, long });
}, error => {
console.error('Couldn\'t get your current position from the browser');
});
}
render() {
return (
<div>
<CoordinatesForm
lat={this.state.lat}
long={this.state.long}
updateCoords={this.updateCoords}
/>
<TimesDisplay lat={this.state.lat} long={this.state.long} />
</div>
);
}
}
ReactDOM.render(<App />, document.querySelector(".app"));
View Compiled