Skip to content

Commit 16a2df2

Browse files
committed
Add some solutions using hooks
1 parent 708591c commit 16a2df2

File tree

2 files changed

+203
-0
lines changed

2 files changed

+203
-0
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// Exercise:
3+
//
4+
// - Create a <GeoPosition> component that encapsulates the geo state and
5+
// watching logic and uses a render prop to pass the coordinates back to
6+
// the <App>
7+
//
8+
// Tip: If you're on a Mac, you may need to enable Location Services in order
9+
// for your browser to determine your current location. Open
10+
// System Preferences > Security & Privacy > Privacy > Location Services
11+
// and make sure the "Enable Location Services" box is checked.
12+
//
13+
// Got extra time?
14+
//
15+
// - Create a <GeoAddress> component that translates the geo coordinates to a
16+
// physical address and prints it to the screen (hint: use
17+
// `getAddressFromCoords(lat, lng).then(address => ...)`)
18+
// - You should be able to compose <GeoPosition> and <GeoAddress> beneath it to
19+
// naturally compose both the UI and the state needed to render it
20+
////////////////////////////////////////////////////////////////////////////////
21+
import React from "react";
22+
import ReactDOM from "react-dom";
23+
import PropTypes from "prop-types";
24+
25+
import LoadingDots from "./LoadingDots";
26+
import getAddressFromCoords from "./utils/getAddressFromCoords";
27+
28+
import { useState, useEffect } from "react";
29+
30+
function useGeoPosition() {
31+
const [/* current state */ geo, /* setState */ updateGeo] = useState(
32+
/* initial state */ {
33+
coords: {
34+
latitude: null,
35+
longitude: null
36+
},
37+
error: null
38+
}
39+
);
40+
41+
useEffect(() => {
42+
const geoId = navigator.geolocation.watchPosition(
43+
position => {
44+
updateGeo({
45+
coords: {
46+
latitude: position.coords.latitude,
47+
longitude: position.coords.longitude
48+
}
49+
});
50+
},
51+
error => {
52+
updateGeo({ error });
53+
}
54+
);
55+
56+
return () => {
57+
navigator.geolocation.clearWatch(geoId);
58+
};
59+
}, []);
60+
61+
return geo;
62+
}
63+
64+
function useAddress(coords) {
65+
const [address, updateAddress] = useState(null);
66+
67+
function refreshAddress() {
68+
updateAddress(null);
69+
70+
const { latitude, longitude } = coords;
71+
72+
if (latitude && longitude) {
73+
getAddressFromCoords(latitude, longitude).then(updateAddress);
74+
}
75+
}
76+
77+
useEffect(refreshAddress, [coords.latitude, coords.longitude]);
78+
79+
return address;
80+
}
81+
82+
/*
83+
const [
84+
// current state
85+
geo,
86+
// setState
87+
updateGeo
88+
] = useState(
89+
// initial state
90+
{
91+
coords: {
92+
latitude: null,
93+
longitude: null
94+
},
95+
error: null
96+
}
97+
);
98+
99+
useEffect(() => {
100+
// componentDidMount and/or componentDidUpdate
101+
return () => {
102+
// componentWillUnmount
103+
}
104+
},
105+
// prop diff in componentDidUpdate
106+
[when, this, stuff, changes, run, it, again]
107+
)
108+
*/
109+
110+
function App() {
111+
const { coords, error } = useGeoPosition();
112+
const address = useAddress(coords);
113+
114+
return (
115+
<div>
116+
<h1>Geolocation</h1>
117+
118+
{error ? (
119+
<div>Error: {error.message}</div>
120+
) : (
121+
<dl>
122+
<dt>Latitude</dt>
123+
<dd>{coords.latitude || <LoadingDots />}</dd>
124+
<dt>Longitude</dt>
125+
<dd>{coords.longitude || <LoadingDots />}</dd>
126+
</dl>
127+
)}
128+
129+
<marquee>
130+
<p>The address is {address || <LoadingDots />}</p>
131+
</marquee>
132+
</div>
133+
);
134+
}
135+
136+
ReactDOM.render(<App />, document.getElementById("app"));
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import { createHashHistory } from "history";
4+
5+
/*
6+
How to use the history library:
7+
8+
// read the current URL
9+
history.location
10+
11+
// listen for changes to the URL
12+
history.listen(() => {
13+
history.location // is now different
14+
})
15+
16+
// change the URL
17+
history.push('/something')
18+
*/
19+
20+
const RouterContext = React.createContext();
21+
22+
import { useState, useEffect, useContext } from "react";
23+
24+
function Router({ children }) {
25+
const history = createHashHistory();
26+
const [location, updateLocation] = useState(history.location);
27+
28+
useEffect(() => {
29+
return history.listen(updateLocation);
30+
}, []);
31+
32+
const handlePush = to => history.push(to);
33+
34+
return (
35+
<RouterContext.Provider value={{ location, push: handlePush }}>
36+
{children}
37+
</RouterContext.Provider>
38+
);
39+
}
40+
41+
function Route({ path, render, component: Component }) {
42+
const { location } = useContext(RouterContext);
43+
44+
if (location.pathname.startsWith(path)) {
45+
if (render) return render();
46+
if (Component) return <Component />;
47+
}
48+
49+
return null;
50+
}
51+
52+
function Link({ to, children }) {
53+
const { push } = useContext(RouterContext);
54+
55+
const handleClick = event => {
56+
event.preventDefault(); // prevent page refresh
57+
push(to);
58+
};
59+
60+
return (
61+
<a href={`#${to}`} onClick={handleClick}>
62+
{children}
63+
</a>
64+
);
65+
}
66+
67+
export { Router, Route, Link };

0 commit comments

Comments
 (0)