From 127b00cb11d3f5c5b7c643a7d89601236f07afa8 Mon Sep 17 00:00:00 2001 From: Ansel Rognlie Date: Fri, 2 Feb 2024 10:45:29 -0800 Subject: [PATCH] add refactor solution files --- src/App.jsx | 39 ++++++++++++------------- src/api/LocationIQ.js | 51 +++++++++++++++++++++++++++++++++ src/components/History.jsx | 12 ++------ src/components/HistoryList.jsx | 11 ++----- src/components/SearchError.jsx | 4 +-- src/components/SearchForm.jsx | 8 +++--- src/components/SearchResult.jsx | 18 ++++-------- src/types/index.js | 7 +++++ 8 files changed, 93 insertions(+), 57 deletions(-) create mode 100644 src/api/LocationIQ.js create mode 100644 src/types/index.js diff --git a/src/App.jsx b/src/App.jsx index 1bcd4e1..f4b1e3a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import axios from 'axios'; +import LocationIQ from './api/LocationIQ'; import SearchForm from './components/SearchForm'; import SearchResult from './components/SearchResult'; import SearchError from './components/SearchError'; @@ -8,7 +8,6 @@ import './App.css'; const API_KEY = import.meta.env.VITE_API_KEY; const BASE_URL = import.meta.env.VITE_BASE_URL; -const SEARCH_URL = 'search.php'; function App() { const [results, setResults] = useState([]); @@ -24,27 +23,25 @@ function App() { const performSearchAsync = (location) => { clearError(); - return axios.get( - `${BASE_URL}${SEARCH_URL}`, { params: - { - key: API_KEY, - q: location, - format: 'json', - }}) - .then(response => { - const { lat, lon } = response.data[0]; - addResult({ - location, - latitude: Number(lat), - longitude: Number(lon), - }); - }) - .catch(error => { - const message = error.response.data.error; - setError(message); - }); + const api = new LocationIQ(API_KEY, BASE_URL); + + return api.getLatLonAsync(location) + .then(result => addResult(result)) + .catch(error => setError(error.message)); }; + // same method in async/await style + // const performSearchAsync = async (location) => { + // clearError(); + // const api = new LocationIQ(API_KEY); + // try { + // const result = await api.getLatLonAsync(location); + // addResult(result); + // } catch (error) { + // setError(error.message); + // } + // }; + const locationSubmitted = (location) => { performSearchAsync(location); }; diff --git a/src/api/LocationIQ.js b/src/api/LocationIQ.js new file mode 100644 index 0000000..50a47cb --- /dev/null +++ b/src/api/LocationIQ.js @@ -0,0 +1,51 @@ +import axios from 'axios'; + +const US_BASE_URL = "https://us1.locationiq.com/v1/"; +const SEARCH_URL = 'search.php'; + +class LocationIQ { + + constructor(apiKey, baseUrl) { + this.apiKey = apiKey; + this.baseUrl = baseUrl || US_BASE_URL; + } + + getLatLonAsync(location) { + return axios.get( + `${this.baseUrl}${SEARCH_URL}`, { params: + { + key: this.apiKey, + q: location, + format: 'json' + }}) + .then(response => { + const { lat, lon } = response.data[0]; + return { location, latitude: Number(lat), longitude: Number(lon) }; + }) + .catch(error => { + const message = error.response.data.error; + throw { message }; + }); + } + + // same method in async/await style + // async getLatLonAsync(location) { + // try { + // const response = await axios.get( + // `${this.baseUrl}${SEARCH_URL}`, { params: + // { + // key: this.apiKey, + // q: location, + // format: 'json' + // }}); + + // const { lat: latitude, lon: longitude } = response.data[0]; + // return { location, latitude, longitude }; + // } catch (error) { + // const message = error.response.data.error; + // throw { message }; + // } + // } +} + +export default LocationIQ; \ No newline at end of file diff --git a/src/components/History.jsx b/src/components/History.jsx index 58b1a63..44abb06 100644 --- a/src/components/History.jsx +++ b/src/components/History.jsx @@ -1,9 +1,7 @@ -import PropTypes from 'prop-types'; +import { ResultType } from '../types'; import './History.css'; -const History = (props) => { - const entry = props.entry; - +const History = ({ entry }) => { return (
  • { entry.location }

    @@ -14,11 +12,7 @@ const History = (props) => { }; History.propTypes = { - entry: PropTypes.shape({ - location: PropTypes.string.isRequired, - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - }).isRequired, + entry: ResultType.isRequired, }; export default History; \ No newline at end of file diff --git a/src/components/HistoryList.jsx b/src/components/HistoryList.jsx index d8f82ea..0487255 100644 --- a/src/components/HistoryList.jsx +++ b/src/components/HistoryList.jsx @@ -1,10 +1,9 @@ import PropTypes from 'prop-types'; import History from './History'; +import { ResultType } from '../types'; import './HistoryList.css'; -const HistoryList = (props) => { - const entries = props.entries; - +const HistoryList = ({ entries }) => { return (

    Search History

    @@ -21,11 +20,7 @@ const HistoryList = (props) => { }; HistoryList.propTypes = { - entries: PropTypes.arrayOf(PropTypes.shape({ - location: PropTypes.string.isRequired, - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - })).isRequired, + entries: PropTypes.arrayOf(ResultType).isRequired, }; export default HistoryList; \ No newline at end of file diff --git a/src/components/SearchError.jsx b/src/components/SearchError.jsx index f1505c2..abf6717 100644 --- a/src/components/SearchError.jsx +++ b/src/components/SearchError.jsx @@ -1,9 +1,7 @@ import PropTypes from 'prop-types'; import './SearchError.css'; -const SearchError = (props) => { - const error = props.error; - +const SearchError = ({ error }) => { if (!error) { return null; } diff --git a/src/components/SearchForm.jsx b/src/components/SearchForm.jsx index 39eea8d..1d1d23f 100644 --- a/src/components/SearchForm.jsx +++ b/src/components/SearchForm.jsx @@ -3,10 +3,10 @@ import PropTypes from 'prop-types'; import './SearchForm.css'; const DEFAULT_STATE = { - location: '', - }; + location: '', +}; -const SearchForm = (props) => { +const SearchForm = ({ onLocationSubmit }) => { const [formValues, setFormValues] = useState(DEFAULT_STATE); const textInput = (e) => { @@ -19,7 +19,7 @@ const SearchForm = (props) => { const formSubmitted = (event) => { event.preventDefault(); - props.onLocationSubmit(formValues.location); + onLocationSubmit(formValues.location); }; return ( diff --git a/src/components/SearchResult.jsx b/src/components/SearchResult.jsx index 58487a9..6b100d3 100644 --- a/src/components/SearchResult.jsx +++ b/src/components/SearchResult.jsx @@ -1,26 +1,20 @@ -import PropTypes from 'prop-types'; +import { ResultType } from '../types'; import './SearchResult.css'; -const SearchResult = (props) => { - const result = props.result; - +const SearchResult = ({ result }) => { return (
    -

    Results for: { result && result.location }

    +

    Results for: { result?.location }

      -
    • Latitude: { result && result.latitude }
    • -
    • Longitude: { result && result.longitude }
    • +
    • Latitude: { result?.latitude }
    • +
    • Longitude: { result?.longitude }
    ); }; SearchResult.propTypes = { - result: PropTypes.shape({ - location: PropTypes.string.isRequired, - latitude: PropTypes.number.isRequired, - longitude: PropTypes.number.isRequired, - }), + result: ResultType, }; export default SearchResult; \ No newline at end of file diff --git a/src/types/index.js b/src/types/index.js new file mode 100644 index 0000000..c0e67c2 --- /dev/null +++ b/src/types/index.js @@ -0,0 +1,7 @@ +import { shape, string, number } from 'prop-types'; + +export const ResultType = shape({ + location: string.isRequired, + latitude: number.isRequired, + longitude: number.isRequired, +});