I am creating a website for my end term project at school.
So far I have two views I am trying to render. Both returning a collection from my express/api endpoint. One behaves sort of correctly and the other completely borks.
I believe my problem is the way I am creating new state in the reducer and knowing when a dumb component has access to props. But before we get to that, here are the errors.
On the not so borky page:
invariant.js:42 Uncaught (in promise) Error: setState(...): takes an object of state variables to update or a function which returns an object of state variables.
And,
Warning: SingleCountry.state: must be set to an object or null
On the second page:
Uncaught (in promise) Error: setState(...): takes an object of state variables to update or a function which returns an object of state variables.
Also I'm getting:
Error: Can't set headers after they are sent.
This happens when I try to navigate to a single page/view from my not so borky first page. That shouldn't be happening as I believe my route is pretty much locked down.
This is from routes/api, I am using Express...
router
.route('/')
.get((req, res, next) => {
return Country.findAll({ include: [Aircraft] })
.then(countries => {
countries.filter(country => {
return country.aircrafts.length > 0;
});
})
.then(countries => res.json(countries))
.catch(next);
})
.post((req, res, next) => {
if (req.body) {
Country.create(req.body)
.then(country => {
country.save();
res.json(country);
})
.catch(next);
}
});
This is the aforementioned reducer:
import { combineReducers } from 'redux';
import axios from 'axios';
const initialState = {
topFiveCountries: [],
oneCountry: {},
countries: [],
};
// ACTION TYPES
const GET_TOP_COUNTRIES_BY_GFI = 'GET_TOP_COUNTRIES_BY_GFI';
const GET_COUNTRY = 'GET_COUNTRY';
const GET_COUNTRIES = 'GET_COUNTRIES';
// ACTION CREATORS
export function getTopFiveCountriesByGFI(topFiveCountries) {
const action = {
type: GET_TOP_COUNTRIES_BY_GFI,
topFiveCountries,
};
return action;
}
export function getCountry(country) {
const action = {
type: GET_COUNTRY,
country,
};
return action;
}
export function getCountries(countries) {
const action = {
type: GET_COUNTRIES,
countries,
};
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);
});
};
}
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);
});
};
}
export function fetchCountries() {
return function thunk(dispatch) {
return axios
.get('/api/countries')
.then(res => res.data)
.then(countries => {
const action = getCountries(countries);
dispatch(action);
});
};
}
// REDUCER
const rootReducer = function(state = initialState, action) {
switch (action.type) {
case GET_COUNTRIES:
return action.countries;
// return { ...state, countries: action.countries };
// return (state.countries = state.countries.concat(action.countries));
case GET_TOP_COUNTRIES_BY_GFI:
// return action.topFiveCountries;
return { ...state, topFiveCountries: action.topFiveCountries };
case GET_COUNTRY:
return action.country;
// return { ...state, oneCountry: action.country };
default:
return state;
}
};
export default rootReducer;
The funny thing the way I am going about them are not too dissimilar for each other. They are both dumb components:
First the TopFiveCountriesByGFI:
import React from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
export default function TopFiveCountriesByGFI(props) {
const topFiveCountries = props.topFiveCountries;
const flagStyle = {
height: '50px',
width: '100px',
};
return (
<div className="row">
<div className="twelve columns">
<h2> - Top Five Coutries By GFI -</h2>
<table className="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>GFI</th>
<th>Flag </th>
</tr>
</thead>
{topFiveCountries.map(function(country) {
return (
<tbody key={country.id}>
<tr>
<td>
<Link className="country-page-link" to={`/countries/${country.id}`}>
{country.name}
</Link>
</td>
<td>{country.GFI}</td>
<td>
<img style={flagStyle} src={country.flagUrl} />
</td>
</tr>
</tbody>
);
})}
</table>
</div>
</div>
);
}
Second is the Countries view:
import React from 'react';
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
export default function Countries(props) {
console.log('props', props.countries);
const countries = props.countries;
const flagStyle = {
height: '50px',
width: '100px',
};
return (
<div className="row">
<div className="twelve columns">
<h2> - Countries -</h2>
<table className="u-full-width">
<thead>
<tr>
<th>Name</th>
<th>GFI</th>
<th>Flag </th>
</tr>
</thead>
{countries &&
countries.map(function(country) {
return (
<tbody key={country.id}>
<tr>
<td>
<Link className="country-page-link" to={`/countries/${country.id}`}>
{country.name}
</Link>
</td>
<td>{country.GFI}</td>
<td>
<img style={flagStyle} src={country.flagUrl} />
</td>
</tr>
</tbody>
);
})}
{/* {countries ? (
countries.map(country => {
return (
<tbody key={country.id}>
<tr>
<td>
<Link className="country-page-link" to={`/countries/${country.id}`}>
{country.name}
</Link>
</td>
<td>{country.GFI}</td>
<td>
<img style={flagStyle} src={country.flagUrl} />
</td>
</tr>
</tbody>
);
})
) : (
<div>Sorry no content yet!</div>
)}*/}
</table>
</div>
</div>
);
}
Any help will be appreciated!
UPDATE As Deryck pointed out one of my endpoints wasn't returning anything but I am still getting this error:
SingleCountry.state: must be set to an object or null
You return an undefined
value inside your promise's then
, which tries to go into res.json(countries)
which then fails/throws. It gets back to the page as undefined
still, resulting in your setState()
issue since there's nothing there.
So, inside of this block
return Country.findAll({ include: [Aircraft] })
.then(countries => {
countries.filter(country => { // <<-------- add `return` before countries
return country.aircrafts.length > 0;
});
})
.then(countries => res.json(countries))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.