Skip to content

Commit 8fe1500

Browse files
committed
Dynamically update pins on map from JSON file (mocking API)
1 parent fed5e8f commit 8fe1500

12 files changed

+94
-84
lines changed

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@
55
"dependencies": {
66
"isomorphic-fetch": "^2.2.1",
77
"leaflet": "^1.2.0",
8+
"prop-types": "^15.5.10",
89
"react": "^15.6.1",
910
"react-dom": "^15.6.1",
1011
"react-leaflet": "^1.4.1",
1112
"react-redux": "^5.0.6",
1213
"react-scripts": "1.0.11",
1314
"react-slick": "^0.14.11",
14-
"redux": "^3.7.2"
15+
"redux": "^3.7.2",
16+
"redux-logger": "^3.0.6",
17+
"redux-thunk": "^2.2.0"
1518
},
1619
"scripts": {
1720
"start": "react-scripts start",

src/actions/action-types.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
export const SEARCH_STORES_BY_POSTCODE = 'SEARCH_STORES_BY_POSTCODE';
2-
export const SEARCH_STORES_NEAR_ME = 'SEARCH_STORES_NEAR_ME';
3-
export const FETCH_STORES_REQUEST = 'FETCH_STORES_REQUEST';
4-
export const FETCH_STORES_FAILURE = 'FETCH_STORES_FAILURE';
5-
export const FETCH_STORES_SUCCESS = 'FETCH_STORES_SUCCESS';
1+
export const REQUEST_STORES = 'REQUEST_STORES';
2+
export const RECEIVE_STORES = 'RECEIVE_STORES';
3+
// export const ERROR_GETTING_STORES = 'ERROR_GETTING_STORES';

src/actions/actions.js

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import * as types from './action-types';
2+
import fetch from 'isomorphic-fetch'
3+
4+
export const requestStores = (postcode) => {
5+
return {
6+
type: types.REQUEST_STORES,
7+
postcode
8+
};
9+
}
10+
11+
export const receiveStores = (postcode, json) => {
12+
return {
13+
type: types.RECEIVE_STORES,
14+
postcode,
15+
stores: json.Stores.StoreDetailsV2Entry,
16+
receivedAt: Date.now()
17+
};
18+
}
19+
20+
21+
// Async actions
22+
23+
export function fetchStores(postcode) {
24+
return (dispatch) => {
25+
dispatch(requestStores(postcode));
26+
27+
const baseURL = (window.location.hostname === 'localhost') ?
28+
'http://localhost:3000':
29+
'https://davidmoodie.com/react-leaflet-store-locator';
30+
31+
const dataPath = 'data/stores-sky2.json';
32+
const dataURL = `${baseURL}/${dataPath}`;
33+
34+
return fetch(dataURL)
35+
.then(response => response.json())
36+
.then(json => dispatch(receiveStores(postcode, json)))
37+
}
38+
}

src/actions/map-actions.js

-36
This file was deleted.

src/components/LocatorContainer.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,19 @@ import Results from './Results';
55
import MapSearch from './MapSearch';
66
import MyMap from './MyMap';
77

8-
import {connect} from 'react-redux';
9-
import {bindActionCreators} from 'redux';
10-
import * as mapActions from '../actions/map-actions';
8+
import { connect } from 'react-redux';
9+
import { bindActionCreators } from 'redux';
10+
import * as actions from '../actions/actions';
1111

1212
class LocatorContainer extends Component {
1313
render() {
14-
const {markers} = this.props;
14+
const markers = this.props.storesFound.map((store) => {
15+
return [parseFloat(store.Latitude), parseFloat(store.Longitude)]
16+
})
1517

1618
return (
1719
<div>
18-
<MapSearch searchStoresByPostcode={this.props.actions.searchStoresByPostcode} />
20+
<MapSearch onSearchPostcode={this.props.actions.fetchStores} />
1921
<button className="btn btn-secondary btn-block mb-4 hidden-sm-up">View Map</button>
2022
<Results />
2123
<MyMap markers={markers} />
@@ -25,19 +27,19 @@ class LocatorContainer extends Component {
2527
}
2628

2729
LocatorContainer.propTypes = {
28-
markers: PropTypes.array.isRequired,
30+
storesFound: PropTypes.array.isRequired,
2931
actions: PropTypes.object.isRequired
3032
};
3133

3234
const mapStateToProps = (state, props) => {
3335
return {
34-
markers: state.map
36+
storesFound: state.storesFound
3537
};
3638
}
3739

3840
const mapDispatchToProps = (dispatch) => {
3941
return {
40-
actions: bindActionCreators(mapActions, dispatch)
42+
actions: bindActionCreators(actions, dispatch)
4143
}
4244
}
4345

src/components/MapSearch.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,24 @@ import PropTypes from 'prop-types'
33
// import './MapSearch.css';
44

55
class MapSearch extends Component {
6-
onSearchLocationsClick = () => {
7-
this.props.searchStoresByPostcode("LS10 123");
6+
onFindStoresClick = () => {
7+
this.props.onSearchPostcode("LS10 123");
88
}
99

1010
render() {
1111
return (
1212
<div className="form-inline search-form">
1313
<label className="sr-only" htmlFor="search-box">Postcode</label>
1414
<input type="text" id="search-box" className="search-box form-control mr-2 mb-4" placeholder="Postcode" />
15-
<button className="btn btn-primary mr-2 mb-4" onClick={this.onSearchLocationsClick}>Find stores</button>
15+
<button className="btn btn-primary mr-2 mb-4" onClick={this.onFindStoresClick}>Find stores</button>
1616
<button className="btn btn-primary mb-4">Near me</button>
1717
</div>
1818
);
1919
}
2020
}
2121

2222
MapSearch.propTypes = {
23-
searchStoresByPostcode: PropTypes.func.isRequired
23+
onSearchPostcode: PropTypes.func.isRequired
2424
};
2525

2626
export default MapSearch;

src/components/MyMap.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ import { Map, Marker, Popup, TileLayer } from 'react-leaflet';
55

66
class MyMap extends Component {
77
render() {
8-
const center = [51.505, -0.09];
9-
const zoom = 13;
8+
const center = [53.7581808159536, -1.57374065678215];
9+
const zoom = 10;
1010
const {markers} = this.props;
11+
1112
const map = (
1213
<Map center={center} zoom={zoom}>
1314
{/* <TileLayer
@@ -18,11 +19,6 @@ class MyMap extends Component {
1819
url='https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png'
1920
attribution='&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="http://cartodb.com/attributions">CartoDB</a>'
2021
/>
21-
{/* <Marker position={position}>
22-
<Popup>
23-
<span>A pretty CSS3 popup.<br/>Easily customizable.</span>
24-
</Popup>
25-
</Marker> */}
2622
{markers.map((position, idx) =>
2723
<Marker key={`marker-${idx}`} position={position}>
2824
<Popup>

src/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom';
33
import './index.css';
44
import App from './App';
55
// import registerServiceWorker from './registerServiceWorker';
6-
import {Provider} from 'react-redux';
6+
import { Provider } from 'react-redux';
77
import configureStore from './store/configure-store';
88

99
const store = configureStore();

src/reducers/index.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import map from './map-reducer.js';
1+
import storesFound from './stores-reducer.js';
22
import {combineReducers} from 'redux';
33

44
const rootReducer = combineReducers({
5-
map
5+
storesFound
66
});
77

88
export default rootReducer;

src/reducers/map-reducer.js

-12
This file was deleted.

src/reducers/stores-reducer.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import * as types from '../actions/action-types';
2+
3+
const storesFound = (state = [], action) => {
4+
switch (action.type) {
5+
case types.REQUEST_STORES:
6+
// Handle isFetching, didInvalidate booleans etc. if applicable
7+
return state;
8+
case types.RECEIVE_STORES:
9+
return action.stores;
10+
default:
11+
return state;
12+
}
13+
};
14+
15+
export default storesFound;

src/store/configure-store.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import {createStore, applyMiddleware} from 'redux';
2+
import thunkMiddleware from 'redux-thunk';
3+
import { createLogger } from 'redux-logger';
14
import rootReducer from '../reducers';
2-
import {createStore, compose} from 'redux';
35

4-
// enable redux devtools
5-
const enhancers = compose(
6-
window.devToolsExtension ? window.devToolsExtension() : f => f
7-
)
6+
const loggerMiddleware = createLogger();
87

9-
export default (initialState) => {
10-
return createStore(rootReducer, initialState, enhancers);
11-
};
8+
export default function configureStore(preloadedState) {
9+
return createStore(
10+
rootReducer,
11+
preloadedState,
12+
applyMiddleware(
13+
thunkMiddleware,
14+
loggerMiddleware
15+
)
16+
)
17+
}

0 commit comments

Comments
 (0)