Created
June 1, 2017 22:08
-
-
Save joshbedo/8ab11d32553918b2840a8b4c23b94fe8 to your computer and use it in GitHub Desktop.
React/Redux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Actions | |
**/ | |
import { | |
VEHICLE_MAKE_CHANGED, | |
VEHICLE_MODEL_CHANGED, | |
VEHICLE_ADD_REQUEST, | |
VEHICLE_ADD_SUCCESS, | |
VEHICLE_ADD_ERROR | |
} from './types'; | |
import axios from 'axios'; | |
import ApiManager from '../api/manager'; | |
export const addVehicleRequest = () => { | |
return { type: VEHICLE_ADD_REQUEST }; | |
}; | |
export const addVehicleSuccess = (vehicle) => { | |
return { type: VEHICLE_ADD_SUCCESS, payload: vehicle }; | |
}; | |
export const addVehicleError = (errors) => { | |
return { type: VEHICLE_ADD_ERROR, payload: errors }; | |
} | |
export const addVehicle = (vehicle, router) => { | |
return (dispatch, getState) => { | |
const path = ApiManager.buildRoute('addVehicle', getState().user.accessToken); | |
dispatch(addVehicleRequest()); | |
axios | |
.post(path, vehicle) | |
.then(response => { | |
dispatch(addVehicleSuccess(response.data)); | |
router.pop(); | |
}) | |
.catch(err => { | |
dispatch(addVehicleError(err.response.data.message)); | |
}); | |
} | |
}; | |
/** | |
* Map Component | |
**/ | |
import React from 'react'; | |
import { | |
StyleSheet, | |
Text, | |
View, | |
ScrollView, | |
Image as ImageNative | |
} from 'react-native'; | |
import { connect } from 'react-redux'; | |
import { Button } from 'react-native-elements'; | |
import { Components } from 'exponent'; | |
import { Container, Content, List, ListItem, Grid, Col, Row, Radio } from 'native-base'; | |
import _ from 'lodash'; | |
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete'; | |
import numeral from 'numeral'; | |
import AddressManager from '../../../api/AddressManager'; | |
import { validateZipCode, setSelectedSavedLocation } from '../../../actions/BookingActions'; | |
import cs from '../../../constants/SharedStyles'; | |
import AddButton from '../../../components/AddButton'; | |
class LocationAdd extends React.Component { | |
static route = { | |
navigationBar: { | |
title: 'Add Location', | |
backgroundColor: '#fff', | |
renderRight: (route, props) => <AddButton name='locationAdd' /> | |
}, | |
} | |
constructor(props) { | |
super(props); | |
this.state = { addressFull: 'Address' }; | |
this.state = { | |
currentPosition: 0 | |
} | |
} | |
componentDidUpdate(prevProps, prevState) { | |
if (this.props.valid && prevProps.lat !== this.props.lat && prevProps.long !== this.props.long) { | |
this.map.fitToSuppliedMarkers(['location'], true); | |
} | |
} | |
goToSchedule = () => { | |
if (this.props.valid) { | |
this.props.navigator.push('schedule'); | |
} | |
} | |
parseAddress = (details, skip = false) => { | |
if (skip === false) { | |
const result = AddressManager.parse(details); | |
if (result && result.postal_code) { | |
this.props.validate(result); | |
} | |
} else { | |
this.props.setAddress(details); | |
} | |
} | |
renderMarker = () => { | |
if (this.props.valid) { | |
return ( | |
<Components.MapView.Marker | |
identifier="location" | |
coordinate={{ | |
latitude: this.props.lat, | |
longitude: this.props.long | |
}} | |
/> | |
); | |
} | |
} | |
render = () => { | |
return ( | |
<View style={cs.container}> | |
<Components.MapView | |
style={{ flex: 1 }} | |
showsCompass={false} | |
ref={ref => { this.map = ref; }} | |
initialRegion={{ | |
latitude: 27.9050697, | |
longitude: -82.675962, | |
latitudeDelta: 0.0622, | |
longitudeDelta: 0.0421, | |
}} | |
> | |
<GooglePlacesAutocomplete | |
placeholder='Enter your address here' | |
minLength={2} // minimum length of text to search | |
autoFocus={true} | |
listViewDisplayed='auto' // true/false/undefined | |
fetchDetails | |
enablePoweredByContainer | |
renderDescription={(row) => row.description} // custom description render | |
onPress={(data, details = null) => { // 'details' is provided when fetchDetails = true | |
let skip = false; | |
if (typeof data.user_id !== 'undefined') { | |
skip = true; | |
} | |
this.parseAddress(details, skip); | |
}} | |
getDefaultValue={() => { | |
return ''; // text input default value | |
}} | |
query={{ | |
// available options: https://developers.google.com/places/web-service/autocomplete | |
key: 'AIzaSyD3QX7tsD1X5UEOgBXQJ4_uekvBQO5Bc3s', | |
language: 'en', // language of the results | |
types: 'address', // default: 'geocode' | |
}} | |
styles={{ | |
textInput: { | |
marginLeft: 10, | |
}, | |
description: { | |
fontWeight: '500', | |
}, | |
predefinedPlacesDescription: { | |
color: '#3498db', | |
}, | |
textInputContainer: { | |
backgroundColor: '#fff', | |
borderTopWidth: 0, | |
borderBottomWidth:0 | |
}, | |
listView: { | |
backgroundColor: '#fff', | |
paddingLeft: 7, | |
}, | |
container: { | |
flex: 0, | |
} | |
}} | |
currentLocation={false} // Will add a 'Current location' button at the top of the predefined places list | |
currentLocationLabel="Current location" | |
nearbyPlacesAPI='None' // Which API to use: GoogleReverseGeocoding or GooglePlacesSearch | |
GoogleReverseGeocodingQuery={{}} | |
// available options for GoogleReverseGeocoding API : https://developers.google.com/maps/documentation/geocoding/intro} | |
GooglePlacesSearchQuery={{ | |
// available options for GooglePlacesSearch API : https://developers.google.com/places/web-service/search | |
rankby: 'distance', | |
types: '(cities)', | |
}} | |
filterReverseGeocodingByTypes={['locality', 'administrative_area_level_3']} // filter the reverse geocoding results by types - ['locality', 'administrative_area_level_3'] if you want to display only cities | |
predefinedPlaces={this.props.locations} | |
debounce={200} // debounce the requests in ms. Set to 0 to remove debounce. By default 200ms. | |
/> | |
{/*}{this.renderMarker()} | |
<View style={[cs.bottom1, cs.bookingNavigationContainer]}> | |
<BookingProcessButtonLocation valid={this.props.valid} /> | |
<View style={[cs.ml1, cs.mr1, cs.mb105, cs.mt105]}> | |
<StepIndicator | |
customStyles={customStyles} | |
currentPosition={this.state.currentPosition} | |
/> | |
</View> | |
</View>*/} | |
</Components.MapView> | |
</View> | |
); | |
} | |
} | |
const customStyles = { | |
stepIndicatorSize: 15, | |
currentStepIndicatorSize:15, | |
separatorStrokeWidth: 0.5, | |
currentStepStrokeWidth: 4, | |
stepStrokeCurrentColor: '#3498db', | |
stepStrokeWidth: 4, | |
stepStrokeFinishedColor: '#3498db', | |
stepStrokeUnFinishedColor: '#bdc3c7', | |
separatorFinishedColor: '#3498db', | |
separatorUnFinishedColor: '#aaaaaa', | |
stepIndicatorFinishedColor: '#3498db', | |
stepIndicatorUnFinishedColor: '#fff', | |
stepIndicatorCurrentColor: '#fff', | |
stepIndicatorLabelFontSize: 1, | |
currentStepIndicatorLabelFontSize: 1, | |
}; | |
const mapStateToProps = state => { | |
let lat = state.booking.new.address.lat; | |
let long = state.booking.new.address.long; | |
if (lat !== '') { | |
lat = numeral(lat).value(); | |
} | |
if (long !== '') { | |
long = numeral(long).value(); | |
} | |
return { | |
valid: state.booking.new.address.valid, | |
lat, | |
long, | |
locations: _.toArray(state.location.list) | |
}; | |
}; | |
const mapDispatchToProps = dispatch => { | |
return { | |
validate: (zipcode) => dispatch(validateZipCode(zipcode)), | |
setAddress: (address) => dispatch(setSelectedSavedLocation(address)) | |
}; | |
}; | |
export default connect(mapStateToProps, mapDispatchToProps)(LocationAdd); | |
/** | |
* Another Component using Redux Form | |
**/ | |
import React from 'react'; | |
import { | |
ScrollView, | |
StyleSheet, | |
Text, | |
View, | |
Dimensions, | |
Picker | |
} from 'react-native'; | |
import {Field, reduxForm as form} from 'redux-form'; | |
import { connect } from 'react-redux'; | |
import Spinner from 'react-native-loading-spinner-overlay'; | |
import { Grid, Col, Row, List, ListItem, ListGroup, Input } from 'native-base'; | |
import cs from '../../../constants/SharedStyles'; | |
import { update } from '../../../actions/PaymentActions'; | |
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view'; | |
@form({ | |
form: 'paymentEdit', | |
validate: (values) => { | |
const errors = {}; | |
if (!values.name) { | |
errors.name = 'Cardholder Name is required'; | |
} | |
if (!values.card_number) { | |
errors.card_number = 'Card Number is required'; | |
} | |
if (!values.exp_month) { | |
errors.exp_month = 'Expiration Month is required'; | |
} | |
if (!values.exp_year) { | |
errors.exp_year = 'Expiration Year is required'; | |
} | |
if (!values.cvc) { | |
errors.cvc = 'CVC is required'; | |
} | |
return errors; | |
} | |
}) | |
@connect((state) => { | |
return { | |
isLoading: state.payment.isLoading | |
} | |
}, { update }) | |
class PaymentEdit extends React.Component { | |
updatePayment = (formProps) => { | |
const router = this.props.navigation.getNavigator('root'); | |
this.props.update(formProps, router); | |
this.props.reset(); | |
} | |
render = () => { | |
const {handleSubmit} = this.props; | |
return ( | |
<View style={[cs.container]}> | |
<KeyboardAwareScrollView> | |
<View> | |
<Grid style={[cs.mt2]}> | |
<Row> | |
<Text style={[cs.ml2, cs.mb1, cs.fw5, cs.color500]}> | |
Edit Card Information | |
</Text> | |
</Row> | |
</Grid> | |
<View> | |
<Field | |
name="name" | |
type="text" | |
component={renderField} | |
label="Cardholder Name" /> | |
<Field | |
name="card_number" | |
type="text" | |
component={renderField} | |
label="Card Number #" /> | |
<Field | |
name="exp_month" | |
type="text" | |
component={renderField} | |
label="Exp Month" /> | |
<Field | |
name="exp_year" | |
type="text" | |
component={renderField} | |
label="Exp Year" /> | |
<Field | |
name="cvc" | |
type="text" | |
component={renderField} | |
label="CVC" /> | |
</View> | |
<Grid style={[cs.mt2, cs.mb2, cs.ml4, cs.mr4, cs.bgP6, cs.radius5, cs.shadow1]} onPress={handleSubmit(this.props.update.bind(this))}> | |
<Row style={[cs.pl2, cs.pr2]}> | |
<Col style={[cs.pt105, cs.pb105]}> | |
<Row style={[cs.selfCenter]}> | |
<Text style={[cs.color000, cs.f4, cs.fw5]}> | |
Update Card | |
</Text> | |
</Row> | |
</Col> | |
</Row> | |
<Spinner visible={this.props.isLoading} /> | |
</Grid> | |
</View> | |
</KeyboardAwareScrollView> | |
</View> | |
); | |
} | |
} | |
const renderField = ({input, label, type, meta: {touched, error, warning}}) => { | |
const renderMessage = () => { | |
return (touched && error) ? <Text style={[cs.colorP3]}>{error}</Text> : null; | |
} | |
return ( | |
<Row style={[cs.pl1, cs.pr1, cs.pt05, cs.pb05, cs.bg000, cs.borderBWidth1, cs.border200]}> | |
<Input | |
{...input} | |
placeholder={label} | |
style={[cs.flx1, cs.fi5, cs.color800]} /> | |
{renderMessage()} | |
</Row> | |
) | |
}; | |
export default PaymentEdit | |
/** | |
* Reducers | |
**/ | |
import { | |
PAYMENT_LIST_FETCH_SUCCESS, | |
PAYMENT_REMOVE_REQUEST, | |
PAYMENT_REMOVE_SUCCESS, | |
PAYMENT_REMOVE_ERROR, | |
PAYMENT_ADD_REQUEST, | |
PAYMENT_ADD_SUCCESS, | |
PAYMENT_ADD_ERROR, | |
PAYMENT_UPDATE_REQUEST, | |
PAYMENT_UPDATE_SUCCESS, | |
PAYMENT_UPDATE_ERROR, | |
} from '../actions/types'; | |
const INITIAL_STATE = { | |
list: [], | |
new: {}, | |
isLoading: false, | |
error: null, | |
}; | |
export default (state = INITIAL_STATE, action) => { | |
switch (action.type) { | |
case PAYMENT_LIST_FETCH_SUCCESS: | |
return { | |
...state, | |
list: action.payload | |
}; | |
case PAYMENT_UPDATE_REQUEST: | |
case PAYMENT_ADD_REQUEST: | |
case PAYMENT_REMOVE_REQUEST: | |
return { ...state, isLoading: true }; | |
case PAYMENT_REMOVE_SUCCESS: | |
return { ...state, list: action.payload, isLoading: false }; | |
case PAYMENT_ADD_SUCCESS: | |
var list = state.list.concat([action.payload]); | |
return { ...state, list: list, isLoading: false }; | |
case PAYMENT_UPDATE_SUCCESS: | |
return { ...state, list: action.payload, isLoading: false }; | |
case PAYMENT_UPDATE_ERROR: | |
case PAYMENT_ADD_ERROR: | |
case PAYMENT_REMOVE_ERROR: | |
return { ...state, error: action.payload, isLoading: false }; | |
default: | |
return state; | |
} | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment