口罩即时查
先附上作品网址及成果展示:口罩即时查
功能(电脑、平板、手机皆可观看)
电脑版 ᴘᴄ
平板 ᴛ
手机版 ᴍ
程式码纪录(Redux-Observable)
上篇文章已经可以成功抓取药局资讯,这边会着重于纪录将药局资讯显示到画面上的过程,那就继续看下去吧!
发送请求的action
/* actions/index.js *///发送 口罩资讯requestexport const fetchMaskInformation = (callback) => { return { type: 'FETCH_MASK_INFORMATION', callback };}//接收口罩资讯export const recieveMaskInformation = (res, updateTime) => { return { type: 'RECIEVE_MASK_INFORMATION', res: res, updateTime: updateTime, };}
epic函数
/* epics/index.js */const maskGetList = (action$) => action$.ofType('FETCH_MASK_INFORMATION').pipe( mergeMap(({callback}) => { return merge( //发送request时设定isLoading为true来告知使用者正在抓取资料 of(actions.maskInformationIsLoading(true)), ajax.getJSON(`网址`).pipe( mergeMap(response => { // console.log(response.features); // 传入callback function if(callback) callback(response.features); // 更新时间 const updateTime = response.features[0].properties.updated; return of( // fetch完之后再次发送action将资料存到reducer actions.recieveMaskInformation(response.features, updateTime), // 收到response后设定isLoading为false告知使用者抓取资料完成 actions.maskInformationIsLoading(false), ); }) ) ) }));
将fetch资料存到reducer调用
/* reducers/index.js */const model = { maskInformationLists: [], updateTime: '',};const Reducer = (state = model, action) => { switch (action.type) { case 'RECIEVE_MASK_INFORMATION': return { ...state, maskInformationLists: action.res, updateTime: action.updateTime, } default: return state; }};export default Reducer;
接下来的部分就是Redux的使用啦,就不再另外纪录Redux的部分。
程式码纪录(Google Map Api)
Google Map 使用的是itsmichaeldiego作者开发的google-map-react套件
安装
npm install --save google-map-react
or
yarn add google-map-react
程式码
...class map extends Component { constructor() { super(); this.state = { center: center, zoom: 16, features: [], }; } ... // 取得目前位置 getLocation = () => { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(this.updateLocation); } } // 取得目前位置后插上marker并且将现在位置设定为中心点 updateLocation = (position) => { this.setState({ center: { lat: position.coords.latitude, lng: position.coords.longitude, } }, () => { // console.log(this.map); // 定位完成后,删掉起始位置的marker this.marker.setMap(null); // 新增现在位置的marker this.marker = new google.maps.Marker({ position: new google.maps.LatLng(this.state.center.lat, this.state.center.lng), map: this.map, // 自订icon图示 icon: location }); this.map.setCenter(this.state.center); this.map.setZoom(16); }); } ... handleApiLoaded = (map) => { this.map = map; // 给定初始位置,如果使用者不允许抓取现在位置时则使用初始位置 this.marker = new google.maps.Marker({ position: new google.maps.LatLng(this.state.center.lat, this.state.center.lng), map: this.map, icon: location, }); // 在Google Map上新增custom button // constructor passing in this DIV. var controlDiv = document.createElement('div'); // Set CSS for the control border. var controlUI = document.createElement('div'); controlUI.style.backgroundColor = '#fff'; controlUI.style.border = '2px solid #fff'; controlUI.style.borderRadius = '3px'; controlUI.style.boxShadow = '0 2px 6px rgba(0,0,0,.3)'; controlUI.style.cursor = 'pointer'; controlUI.style.marginRight = '10px'; controlUI.style.textAlign = 'center'; controlUI.title = 'Click to recenter the map'; controlDiv.appendChild(controlUI); // Set CSS for the control interior. var controlText = document.createElement('div'); controlText.style.color = 'rgb(25,25,25)'; controlText.style.fontFamily = 'Roboto,Arial,sans-serif'; controlText.style.fontSize = '16px'; controlText.style.lineHeight = '38px'; controlText.style.paddingLeft = '5px'; controlText.style.paddingRight = '5px'; controlText.innerHTML = `<img src=${centerGPS} alt=""></img>`; controlUI.appendChild(controlText); // Setup the click event listeners: simply set the map to Chicago. controlUI.addEventListener('click', this.getLocation); this.map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(controlDiv); // ApiLoaded完之后才fetch this.props.fetchMaskInformation(this.createMarker); this.getLocation(); }; createMarker = () => { // 在此function插marker、建立infowindows动作、marker cluster ... } render() { const { classes } = this.props; return ( <div className={classes.container}> <GoogleMapReact bootstrapURLKeys={{ key: /* YOUR KEY HERE */ }} defaultCenter={center} defaultZoom={this.state.zoom} yesIWantToUseGoogleMapApiInternals onGoogleApiLoaded={({ map, maps }) => this.handleApiLoaded(map, maps)} > </GoogleMapReact> </div> ); }}
参考来源:
google-map-react
Maps JavaScript API
以上是这次口罩练习的纪录,之后如果还有加上typescript的话会再补充上来。
心得
透过这次的口罩练习确实的把Redux及Observable好好的实作一次,从中更了解了从发送request到接收到response再到呈现到画面上的整个流程之外,也在途中发生了一个小插曲,让我体会到Observable的方便之处,也觉得自己的战斗力提升了不少XD
那就来分享个小插曲,前几篇文有提到Observable的优点以及分享了Netflix发表的演讲影片来叙述为什么要使用Observable,当时只是知道说原来有这样的优点,但还是抱持着真的有这么好用吗的想法,这次在处理药局资讯的response时我就因为同时发送fetch以及Google map api产生了不同步行为,要是fetch先完成那没问题,地图会正常显示并且插上marker,但若是因为网路问题或是其他因素导致fetch较慢完成,就会造成地图先loaded完成,却没有插上各个药局的marker,所以使用Observable就能轻鬆地解决这个问题,这就是Observable的强大啊!!
问题与讨论
我也是前端的小菜鸟,所以要是有什么写得不好的地方,大师路过还请多给我一些指点,也请各位大师鞭小力点XD
如果你/妳已经是在前端上有一两年经验的,因本系列文章偏向前端入门,可能本系列文章不太适合你/妳,但也欢迎你/妳给予一些建议。