Reputation: 7739
I am building an app using React, React-redux, Redux, Express, and Sequelize for a project for school.
In my other views I got the data back, to insert and update my view accordingly, but in this case I'm getting undefined.
I just switched from redux to react-redux so I apologize in advance!
This is a screenshot of my console:
This my reducer for the SingleCountry, with my action creators and thunks...
'use strict';
import axios from 'axios';
// ACTION TYPES
const GET_COUNTRY = 'GET_COUNTRY';
// ACTION CREATORS
export function getCountry(oneCountry) {
const action = {
type: GET_COUNTRY,
oneCountry,
};
return action;
}
//THUNK CREATORS
export function fetchCountry(countryId) {
return function thunk(dispatch) {
return axios
.get('/api/countries/' + `${countryId}`)
.then(res => res.data)
.then(country => {
const action = getCountry(country);
dispatch(action);
});
};
}
// REDUCER
const reducer = function(state = [], action) {
switch (action.type) {
case GET_COUNTRY:
return action.oneCountry;
default:
return state;
}
};
export default reducer;
This is the reducer for Countries:
'use strict';
import axios from 'axios';
// ACTION TYPES
const GET_COUNTRIES = 'GET_COUNTRIES';
// ACTION CREATORS
export function getCountries(countries) {
const action = {
type: GET_COUNTRIES,
countries,
};
return action;
}
//THUNK CREATORS
export function fetchCountries() {
return function thunk(dispatch) {
return axios
.get('/api/countries')
.then(res => res.data)
.then(countries => dispatch(getCountries(countries)))
.catch(console.error);
};
}
// REDUCER
const reducer = function(state = [], action) {
switch (action.type) {
case GET_COUNTRIES:
return action.countries;
// return { ...state, countries: action.countries };
default:
return state;
}
};
export default reducer;
And this is the reducer for the TopFiveCountries:
'use strict';
import axios from 'axios';
// ACTION TYPES
const GET_TOP_COUNTRIES_BY_GFI = 'GET_TOP_COUNTRIES_BY_GFI';
// ACTION CREATORS
export function getTopFiveCountriesByGFI(topFiveCountries) {
const action = {
type: GET_TOP_COUNTRIES_BY_GFI,
topFiveCountries,
};
return action;
}
//THUNK CREATORS
export function fetchTopFiveCountriesByGFI() {
return function thunk(dispatch) {
return axios
.get('/api/countries/top-five-countries')
.then(res => res.data)
.then(countries => {
const action = getTopFiveCountriesByGFI(countries);
dispatch(action);
})
.catch(console.error);
};
}
// REDUCER
const reducer = function(state = [], action) {
switch (action.type) {
case GET_TOP_COUNTRIES_BY_GFI:
return action.topFiveCountries;
// return { ...state, topFiveCountries: action.topFiveCountries };
default:
return state;
}
};
export default reducer;
This the view for SingleCountry question:
import React, { Component } from 'react';
import { fetchCountry } from '../reducers/oneCountry';
import { connect } from 'react-redux';
import store from '../store';
class SingleCountry extends Component {
constructor(props) {
super(props);
}
render() {
const flagStyle = {
height: '50px',
width: '100px',
};
const oneCountry = this.props.oneCountry;
return (
<div className="row">
<div className="twelve columns">
<h2> - Single Country -</h2>
<table className="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>GFI</th>
<th>Flag </th>
</tr>
</thead>
<tbody>
<tr>
<td>{oneCountry.name}</td>
<td>{oneCountry.GFI}</td>
<td>
<img style={flagStyle} src={oneCountry.flagUrl} />
</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}
}
const mapDispatch = dispatch => ({
fetchCountry: countryId => {
dispatch(fetchCountry(countryId));
},
});
const mapState = state => ({
oneCountry: state.oneCountry,
});
export default connect(mapState, mapDispatch)(SingleCountry);
My gut is telling me its either in my axios request or maybe some of the syntax for mapping and dispatching.
As an aside I do at least get a partial render (see below)...And no errors. Are there any precautions one could do for better diagnose?
Any help will be appreciated!
UPDATE
This is how my store.js
file looks like...
import { createStore, applyMiddleware, combineReducers } from 'redux';
import loggingMiddleware from 'redux-logger'; // https://github.com/evgenyrodionov/redux-logger
import thunkMiddleware from 'redux-thunk'; // https://github.com/gaearon/redux-thunk
import { composeWithDevTools } from 'redux-devtools-extension';
import topFiveCountriesByGFIReducer from './reducers/topFiveCountriesByGFI';
import countriesReducer from './reducers/countries';
import oneCountryReducer from './reducers/oneCountry';
const middleware = [loggingMiddleware, thunkMiddleware];
const rootReducer = combineReducers({
topFiveCountries: topFiveCountriesByGFIReducer,
countries: countriesReducer,
oneCountry: oneCountryReducer,
});
export default createStore(rootReducer, composeWithDevTools(applyMiddleware(...middleware)));
UPDATE
Recent error I'm getting:
Upvotes: 0
Views: 745
Reputation: 57964
So you aren't dispatching your action. You have to dispatch the action, and since it's asynchronous, it'd be good to show some kind of UI to indicate you're fetching from an API of some sort. Here's a trimmed down version:
class SingleCountry extends Component {
constructor(props) {
super(props)
this.state = {
loading: true
}
}
componentDidMount() {
const countryId = this.props.match.params.id;
dispatch(this.props.fetchCountry(countryId))
.then(() => {
this.setState({
loading: false
});
});
}
render() {
const oneCountry = this.props.oneCountry;
const loading = this.state.loading;
return (
<div className="row">
<div className="twelve columns">
<h2> - Single Country -</h2>
{
!loading ?
<table className="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>GFI</th>
<th>Flag </th>
</tr>
</thead>
<tbody>
<tr>
<td>{oneCountry.name}</td>
<td>{oneCountry.GFI}</td>
<td>
<img style={flagStyle} src={oneCountry.flagUrl} />
</td>
</tr>
</tbody>
</table>
:
<img src={LOADING ICON HERE} />
}
</div>
</div>
);
}
}
const mapState = (state, ownProps) => ({
oneCountry: state.oneCountry,
...ownProps
});
What this does is initially set state of loading
to true. Then, when the component mounts, your request is sent to the API. Meanwhile, while the request is working, a loading icon will show up. Once the request is done, the then
of the dispatch will execute and set state of loading
to false. This will, in turn, rerender your component and show the table, with the appropriate data.
Also, notice mapState
now uses a second argument - ownProps
. The thing is, when you connect
a component to the Redux store, you lose any props that are passed to the component. In this case, React Router passes the matched parameters to SingleCountry
but it's lost due to connect
. This can be solved by using the second argument of either mapState
or mapDispatch
. The second argument contains allow the props passed to the connected component, and you can spread them into the returned object to access them in your component.
Upvotes: 1