<file_sep>## Intro

* Try to make this app in es6: https://scotch.io/tutorials/setup-a-react-environment-using-webpack-and-babel

## Ref

* https://scotch.io/tutorials/setup-a-react-environment-using-webpack-and-babel
* http://demo.tutorialzine.com/2015/04/first-webapp-react/
* http://tutorialzine.com/2015/04/first-webapp-react/
* https://github.com/MorganLKestner/react-location-search-app

## Install
yarn install

## Run
yarn start, then visit localhost:8080
<file_sep>import { TOGGLE_FAV_LOCATION, IS_FAV } from "./types";
import isAddressInFav from "../utils/isAddressInFav";


export function toggleFavLocation(currAddress, lat, lng) {

  // https://stackoverflow.com/questions/39257740/how-to-access-state-inside-redux-reducer
  return (dispatch, getState) => {

    // get currAddr and addrArr
    const { toggleFavLocation } = getState();

    // is fav
    let isFav;

    // fav array
    let favArr = toggleFavLocation.favArr;
    // is it in state.
    let index = isAddressInFav(favArr, currAddress);
    let timenow = Date.now();
    let newArr = [];

    if(index !== -1) {
      // found, remove
      isFav = false;

      // because any obj in javascript is ref
      // if change ref obj, individual data within state cannot be touched.
      // https://stackoverflow.com/questions/34582678/is-this-the-correct-way-to-delete-an-item-using-redux
      newArr = [
        ...favArr.slice(0, index),
        ...favArr.slice(index + 1)
      ];

      dispatch({
        type: TOGGLE_FAV_LOCATION,
        currAddress: currAddress,
        favArr: newArr
      });

      // store
      localStorage.favArr = JSON.stringify(newArr);
    }
    else {
      // not there, add
      isFav = true;

      // https://stackoverflow.com/questions/40911194/how-do-i-add-an-element-to-array-in-reducer-of-react-native-redux
      // currAddr and addr array
      newArr = [...favArr, {
        address: currAddress,
        timestamp: Date.now(),
        lat: lat,
        lng: lng
      }];

      dispatch({
        type: TOGGLE_FAV_LOCATION,
        currAddress: currAddress,
        favArr: newArr
      });

      // store
      localStorage.favArr = JSON.stringify(newArr);
    }

    // the star
    dispatch({
      type: IS_FAV,
      currAddress: currAddress,
      isFav: isFav
    });

  };
}
<file_sep>import React from 'react';
import Search from './Search';
import Map from "./Map";
import CurrentLocation from "./CurrentLocation";

import ListLocation from "./ListLocation";


export default class App extends React.Component {
  render() {
    return (
      <div>
        <h1>My react map</h1>
        <Search />
        <Map />
        <CurrentLocation />
        <ListLocation />
      </div>
    );
  }
}
<file_sep>import React from 'react';
import { connect } from "react-redux";
import { search } from "../actions/search";

class Search extends React.Component {
  
  constructor(props) {
    // super props
    super(props);

    this.state = {
      // need to match up the name of input
      currAddress: this.props.currAddress
    };

    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
  }
  
  
  onSubmit(e) {
    e.preventDefault();
    
    // it seems props not changing
    // so use this.state.xxxx
    this.props.search(this.state.currAddress);
  }
  
  onChange(e) {
    this.setState({
      [e.target.name]: e.target.value
    });
  }


  render() {
    //console.log("-- search input box render --");
  
    // value, this.state.search
    return (
      <form id="geocoding_form" className="form-horizontal" onSubmit={this.onSubmit}>
				<div className="form-group">
					<div className="col-xs-12 col-md-6 col-md-offset-3">
						<div className="input-group">
							<input
							  name="currAddress" 
							  type="text" 
							  className="form-control" 
							  id="address" 
							  placeholder="Find a location..." 
							  
							  value={this.state.currAddress}
							  onChange={this.onChange} 
							/>
							
							<span className="input-group-btn">
								<span className="glyphicon glyphicon-search" aria-hidden="true"></span>
							</span>
						</div>
					</div>
				</div>
			</form>
    );
  }
}

function mapStateToProps(state) {
  return {
    currAddress: state.search.currAddress
  };
}

export default connect(mapStateToProps, { search })(Search);
<file_sep>import React from 'react';
import { connect } from "react-redux";
import SingleLocation from "./SingleLocation";


class ListLocation extends React.Component {

  constructor(props) {
    super(props);

    //console.log("construcotr....");
  }

  render() {
    console.log("--- ListLocation.js, render ---");
    console.log(this.props.propFavArr);

    // yes index
    let locations = this.props.propFavArr.map((loc, index) => {

      var active = this.props.propCurrAddress == loc.address;

      // need to return
      // have to return in array
      return <SingleLocation
        key={index}
        address={loc.address}
        lat={loc.lat}
        lng={loc.lng}
        
        active={active}
        timestamp={loc.timestamp}
      />
    });

    return (
      <div className="list-group col-xs-12 col-md-6 col-md-offset-3">
        <span className="list-group-item active">Saved Locations</span>
        { locations }
      </div>
    );
    
  }

}


function mapStateToProps(state) {

  //test
  //console.log("--- ListLocation.js, mapStateToProps ---");
  //console.log(state.toggleFavLocation.favArr);

  return {
    // The way to think about is multiple components can share a single reducer
    propCurrAddress: state.search.currAddress,
    propFavArr: state.toggleFavLocation.favArr
  };
}

export default connect(mapStateToProps, {  })(ListLocation);
<file_sep>export default function isAddressInFavLocalStorage(address) {
  let condi = false;
  let favArr = [];

  if(localStorage.favArr) {
    favArr = JSON.parse(localStorage.favArr);
    
    for(let i=0; i < favArr.length; i++) {
      if(favArr[i].address === address) {
        condi = true;
        break;
      }
      else {
      
      }
    }
  }
  else {
    
  }
  
  return condi;
}
<file_sep>export default function favAddressInLocalStorage() {
  let favArr = [];

  if(localStorage.favArr) {
    favArr = JSON.parse(localStorage.favArr);
  }
  else {
    favArr = [];
  }
  
  return favArr;
}
<file_sep>// the types.js has many constant.
import { CLICK_SINGLE_LOCATION, SEARCH } from "./types";

export default function clickSingleLocation(currAddress, lat, lng) {

  // https://www.youtube.com/watch?v=1QI-UE3-0PU
  return (dispatch, getState) => {
    
    //test
    //console.log("---- clickSingleLocation ----");
    //console.log(currAddress);
    //console.log(lat);
    //console.log(lng);
    
    // search, map, list location re-render
    // single location gets re-render as well, because the parent ListLocation gets re-render.
    dispatch({ 
      type: SEARCH, 
      currAddress: currAddress,
      lat: lat,
      lng: lng
    });
 
    
  }
  
}



<file_sep>import React from 'react';
import { connect } from "react-redux";

class Map extends React.Component {
  
  constructor(props) {
    //console.log("---- constructor: map state to props, call before constructor ----");
  
    // super props
    super(props);
  }

  componentDidMount() {
    //console.log("---- in componentDidMount ----");
  
    // init
    var map = new GMaps({
      el: '#map',
      lat: this.props.lat,
      lng: this.props.lng
    });

    map.addMarker({
      lat: this.props.lat,
      lng: this.props.lng
    });
    
  }

  // component did update, won't be called when page reload.
  componentDidUpdate() {
    
    //test
    //console.log("---- in componentDidUpdate ----");
    
    var map = new GMaps({
      el: '#map',
      lat: this.props.lat,
      lng: this.props.lng
    });

    map.addMarker({
      lat: this.props.lat,
      lng: this.props.lng
    });
  }

  render() {
  
    return (
      <div className="map-holder">
        <p>Loading...</p>
        <div id="map"></div>
      </div>
    );
  }
  
}


function mapStateToProps(state) {

  //test
  // The first time global state is empty, because no one changes global state.
  // When someone does something on UI, global state changes. 
  //console.log("---- mapStateToProps ----");
  //console.log(state);

  // This will together with the props pass down
  // you cannot call state.lat.
  // because you need to use the reducer as identifier.
  // e.g. state.search.lat or state.toggleFavLocation.toggleFavLocation
  return {
    lat: state.search.lat,
    lng: state.search.lng
  };
}


export default connect(mapStateToProps, {  })(Map);
<file_sep>import React from 'react';
import { connect } from "react-redux";
import moment from 'moment';
import clickSingleLocation from "../actions/clickSingleLocation";

class SingleLocation extends React.Component {
  
  constructor(props) {
    super(props);
    
    this.onClick = this.onClick.bind(this);
  }
  
  // don't call onClick()
  onClick(e) {
    e.preventDefault();
    this.props.clickSingleLocation(this.props.address, this.props.lat, this.props.lng);
  }
  
  render() {
    let cn = "list-group-item";

    if (this.props.active) {
      cn += " active-location";
    }
  
  
    // render and return
    // render and return
    return(
      <a className={cn} onClick={ this.onClick }>
        {this.props.address}
        &nbsp;----&nbsp;
        <span className="createAt">{ moment(this.props.timestamp).fromNow() }</span>
        <span className="glyphicon glyphicon-menu-right"></span>
      </a>
    )
  }
  
}


export default connect(null, { clickSingleLocation })(SingleLocation);
<file_sep>import { CLICK_SINGLE_LOCATION } from "../actions/types";

const initState = {
  currAddress: ""
};


// able to fire
export default function clickSingleLocation(state = initState, action = {}) {
  switch(action.type) {
  
    case CLICK_SINGLE_LOCATION:
      console.log("--- clickSingleLocation, reducer ----");
    
      return {
        currAddress: action.currAddress,
      }

    default:
      return state;
  }
}

<file_sep>import { IS_FAV } from "./types";
import isAddressInFav from "../utils/isAddressInFav";


export function isFav(currAddress) {

  // https://stackoverflow.com/questions/39257740/how-to-access-state-inside-redux-reducer
  return (dispatch, getState) => {
     
    const { toggleFavLocation } = getState();
    
    // add or remove from local storage
    let favArr = toggleFavLocation.favArr;
    let isFav;
    let index = isAddressInFav(favArr, currAddress);
    
    
    if(index !== -1) {
      // found, remove
      isFav = false;
    }
    else {
      // not there, add
      isFav = true;
    }
    
    // https://github.com/reactjs/redux/issues/1676
    //console.log("---- in isFav ----");
    //console.log(isFav);
    
    dispatch({ 
      type: IS_FAV, 
      currAddress: currAddress,
      isFav: isFav
    });
    
  };
}
