I have been trying to make a simple URL request to a third party API. Here is the project I am working on (https://github.com/JMStudiosJoe/ReactPractice/tree/JMStudiosReact). I have been watching Dan Abramov videos, followed this example from redux docs and this example and seem to be missing something small since the logs show that my data from the API was recieved and dispatched to the reducer but the state for some reason does not get sent back to the component. Below is a summary of code:
//app.tsx: get the store and use Provider from react-redux
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import store from './redux/store/store'
import { Provider } from 'react-redux'
import { getAddressData } from './redux/actions/voteSmartActions'
import {UIRouter, UIView, UISref, UISrefActive, pushStateLocationPlugin} from 'ui-router-react';
import NavigationBarComponent from './components/navigationBarComponent'
store.dispatch(getAddressData(''))
ReactDOM.render(
<Provider store={store}>
<NavigationBarComponent userType='buyer' />
</Provider>,
document.getElementById("root")
);
The component in question gets loaded from the projectsComponent.tsx file
if(projectName == 'Vote Smart Locally' ) {
return (
<VoteSmartLocallyComponent />
)
}
//voteSmartLocallyComponent.tsx
import * as React from 'react'
import { connect } from 'react-redux'
import { getAddressData } from '../../redux/actions/voteSmartActions'
import store from "../../redux/store/store"
interface VoteSmartState {
address: string
userAddressData?: any
}
interface VoteSmartProps {
fetchAddressData: any
}
const API_KEY = 'AIzaSyCWhwRupMs7IeE4IrGEgHtT0Nt-IGZnP9E'
const endURL = '&key='+ API_KEY
const baseRepURL = 'https://www.googleapis.com/civicinfo/v2/representatives?address='
const baseElectionsURL = 'https://www.googleapis.com/civicinfo/v2/elections?alt=json&prettyPrint=true'
class VoteSmartLocallyComponent extends React.Component<VoteSmartProps, VoteSmartState> {
constructor(props) {
super(props)
console.log(props)
this.state = {
address: '',
userAddressData: {}
}
}
removeSpacesAddPluses() {
return this.state.address.split(' ').join('+')
}
lookupAddress(event: React.MouseEvent<HTMLButtonElement>) {
event.preventDefault()
const address = this.removeSpacesAddPluses()
const fullRepURL = baseRepURL + address + endURL
const fullElectionsURL = baseElectionsURL + address + endURL
this.props.fetchAddressData(fullRepURL)
/*
store.subscribe(this.render)
store.dispatch({
type: 'LOOKUP_ADDRESS',
payload: address
})
*/
}
handleAddress(event: React.ChangeEvent<HTMLInputElement>) {
event.preventDefault()
const address = event.target.value
this.setState({
address: address
})
}
render() {
return (
<div>
{console.log('log in the render method')}
{console.log(this.state)}
vote smart kids
need to connect the redux and suff to make request
<input
type='text'
placeholder='Address'
onChange={ e => this.handleAddress(e) }
/>
<button
onClick={ e => this.lookupAddress(e) }
>
Submit for info
</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
address: '',
userAddressData: {}
}
}
const mapDispatchToProps = (dispatch) => {
return {
fetchAddressData: (url) => dispatch(getAddressData(url))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(VoteSmartLocallyComponent)
I am able to call this.props.fetchAddressData(fullRepURL)
which calls my action
//voteSmartActions.tsx
import {Action} from 'redux'
import store from '../store/store'
import axios from 'axios'
interface VoteSmartAction<Action> {
type: string
payload?: any
}
const getAddressData = (fullURL: string) => {
return function(dispatch, getState) {
if (fullURL !== '') {
return axios.get(fullURL).then(function (response) {
dispatch(addressDataSuccess(response))
}).catch(function (error) {
console.log(error)
})
}
}
}
const addressDataSuccess = (addressData: any) => {
return {
type: 'HANDLE_RETURN_DATA',
payload: addressData,
addressData
}
}
export {
getAddressData, VoteSmartAction
}
And from there goes to my reducer
//voteSmartReducer.tsx
import {Action} from 'redux'
import {VoteSmartAction, getAddressData} from '../actions/voteSmartActions'
import axios from 'axios'
const INITIAL_STATE: any = {
address: '',
userAddressData: {}
}
const VoteSmartReducer = (state: any = INITIAL_STATE, action: VoteSmartAction<Action>): any => {
switch(action.type) {
case "HANDLE_RETURN_DATA":
console.log("in reducer handling return payload is")
const returnData = {
...state,
userAddressData: action.payload
}
console.log(returnData)
return returnData
default:
return state
}
}
export default VoteSmartReducer
From there the state I made in the reducer should be returned back to the component with the data I fetched but it isn't. I would appreciate any advice or help, thanks.
Currently you're passing in an empty object for userAddressData
and empty string for address
in mapStateToProps. So your component will always have these values.
You need specify within mapStateToProps, where the data lies within the state tree.
Look at the shape of your reducer, and see where where the data sits within the state tree, and then map it in the following way within mapStateToProps.
Example
index.js
import { combineReducers } from 'redux'
import voteSmartReducer from './VoteSmartReducer' // assuming this is in same directory
import someOtherReducer from './someOtherReducer' // example purposes
export default combineReducers({
userAddressData: voteSmartReducer,
otherExample: someOtherReducer
})
As you can see, the data returned by the voteSmartReducer is mapped to the key userAddressData in combineReducers. So state.userAddressData
points to this. This is how you would map the state to props.
const mapStateToProps = (state) => {
return {
userAddressData: state.userAddressData,
otherExample: state.otherExample
}
}
When you create the store, you import the reducer from index.js and pass it in as the first argument to createStore
.
Example.
import { createStore } from 'redux'
import reducers from './index.js'
const store = createStore(
reducers
)