React error .map() is not a function after updating API data - reactjs

I am currently working on a project where my react app connects to my own API. I've run into a problem that I can't seem to overcome. When I try to update my data I get an error saying map is not a function.
this is my vehicleOverview.js script this is supposed to show a list of all my available data which works fine until a I update a data element.
export class VehicleOverview extends React.Component{
constructor(props){
super(props);
this.state= {
vehicles: [],
}
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message,
});
}).catch((error) => {
console.error(error);
});
}
render(){
return(
<div >
<h2>Vehicle List</h2>
<ul >
{
this.state.vehicles.map((vehicle, i) => {
return(
<Vehicle key={`vehicle-${i}`} {...vehicle} />);
})
}
</ul>
</div>
);
}
}
export default VehicleOverview;
I am not sure what is going wrong because I am pretty sure vehicles is an array. Tell me if I need to provide more code to solve this problem

I noticed two things here:
Using this.setState in constructor is not recommended, because component is yet to be rendered.
Always do api calls from componentDidMount - For the reasons mentioned in this article
Also, just in case, add a condition before looping through vehicles.
import React from 'react';
export class VehicleOverview extends React.Component {
constructor(props) {
super(props);
this.state = {
vehicles: []
};
}
componentDidMount() {
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message
});
}).catch((error) => {
console.error(error);
});
}
render() {
return (
<div >
<h2>Vehicle List</h2>
<ul>
{this.state.vehicles && this.state.vehicles.map((vehicle, i) => (<Vehicle key={`vehicle-${i}`} {...vehicle} />))}
</ul>
</div>
);
}
}
export default VehicleOverview;

.map() function will work only for Arrays data.
check what typeof response you are getting.

Related

React component sometimes don't show up

I'm in process of learning React (currently high order component) and I have one problem. In my exercise, component sometimes display data and sometimes don't. My code consist of two Component
DisplayList - component that show data passed by props
GetAndDisplayData - high order component that receive DisplayList component, url of API, and desired parameter (to take from API)
Code:
DisplayList.js
class DisplayList extends React.Component{
render(){
return(
<ul>
{this.props.data.map((input, i) => <li key={i}>{input}</li>)}
</ul>
);
}
}
GetAndDisplayData.js
const GetAndDisplayData = (DisplayList, urlOfData, parameterToGet) =>
class GetAndDisplayDataClass extends React.Component{
constructor(props){
super(props);
this.state = {
loading: true,
urlOfData: urlOfData,
parameterToGet: parameterToGet,
data: []
}
}
componentDidMount(){
this.getData(urlOfData, parameterToGet)
}
getData(urlOfData,parameterToGet){
fetch(urlOfData)
.then(data => data.json())
.then(jsonData => {
jsonData.map(input =>
this.state.data.push(eval("input."+parameterToGet))
);
})
this.setState({loading: false})
console.log(this.state.data)
}
render(){
if(this.state.loading){
return(<p>Data is loading</p>)
}else{
return(
<div>
<p>Data loaded</p>
<DisplayList data={this.state.data} />
</div>
);
}
}
}
And call of HOC
render(){
const GetData = GetAndDisplayData(DisplayList, "https://restcountries.eu/rest/v1/all", "name" );
return(
<div>
<GetData/>
</div>
);
I suppose that problem is something about asynchronous, beacuse if I use some short list of data everthing is working great, but if I use this API and list of 250 object in list, sometimes data don't show up (and sometimes does). What am I doing wrong?
As you already said, data loading is asynchronous, so you must update loading state variable inside the callback function :
componentDidMount(){
this.loadData(urlOfData, parameterToGet)
}
loadData(urlOfData, parameterToGet){
fetch(urlOfData)
.then(data => data.json())
.then(jsonData => {
// I didn't understand how you want to map the data
const data = jsonData.map(...);
console.log(data);
// you must update the state here
this.setState({loading: false, data: data});
});
}

Child component's state does not change while rendering

I am a novice to ReactJs so please bear with me. I am trying to build a project from this course called Git-Hub profile viewer. Here is my code for Parent component:
import Profile from './github/Profile.jsx';
class App extends Component {
constructor(props){
super(props);
this.state = {
username: 'xxxx',
userData: [],
userRepos: [],
perPage: 5
}
}
// get user data from github
getUserData(){
$.ajax({
url: 'https://api.github.com/users/' +this.state.username+'?client_id='+this.props.clientId+'&client_secret='+this.props.clientSecret,
dataType:'json',
cache:false,
success: function(data){
this.setState({userData:data});
console.log(data);
}.bind(this),
error: function(xhr, status, err){
this.setState({userData: null});
alert(err);
}.bind(this)
});
}
componentDidMount(){
this.getUserData();
}
render(){
return (
<div>
<Profile userData = {this.state.userData} />
</div>
)
}
}
App.propTypes = {
clientId: React.PropTypes.string,
clientSecret: React.PropTypes.string
};
App.defaultProps = {
clientId: 'some_genuine_client_Id',
clientSecret:'some_really_client_secret'
}
export default App;
and here is my child's component:
class Profile extends Component {
render() {
return (
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">{this.props.userData.name}</h3>
</div>
<div className="panel-body">
</div>
</div>
)
}
}
export default Profile;
Problem is that the props in child component does not update its state while rendering the page although the data retrieval is successful from Git-Hub as shown in console log. What am I doing wrong, can somebody please help?
So firstly, you should be ensuring that you are accessing userData in the correct way, as #mersocarlin mentioned. This is quite likely to be the cause of your problem.
Theoretically, your way of doing this should work. However I've written a working jsfiddle for you as an example. I couldn't make the Ajax call so have simulated it with a setTimeout event over 5 seconds.
The other way of doing this is to pass down the getUserData function to the child component (Profile) and let it handle the call itself. Here is the jsfiddle for that, and the code below:
class Profile extends React.Component {
constructor(props) {
super(props);
this.state = {
userData: {}
};
}
componentDidMount() {
const userData = this.props.getUserData();
console.log(userData);
this.setState({ userData: userData });
}
render() {
return (
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title">Hello, {this.state.userData.name || 'World'}</h3>
</div>
<div className="panel-body">
</div>
</div>
)
}
}
class App extends React.Component {
getUserData(){
return {
'name': 'Tom'
}
}
render(){
return (
<Profile getUserData={this.getUserData} />
)
}
};
First of all, you should call getUserData() in componentWillMount rather than componentDidMount to get the data before the component has rendered. And then in the child component use a life cycle method componentWillReceiveProps to check if the child component is getting the right props and updating the state accordingly. It'll be something like this:
componentWillReceiveProps(nextProps) {
if(this.props.userData !== nextProps.userData) {
console.log('condition met');
this.setState({
userData: nextProps.userData
});
}
}
Try this and let me know, I'll help you further.

Fetch data periodically from server and re-render view in reactjs

I want to fetch data from server periodically and refresh rows when data is fetched by setState() but the rows doesn't re-render after setState().
constructor(props) {
super(props);
this.state = {
rows: []
}
this.refreshList = this.refreshList.bind(this);
}
refreshList() {
req.get('/data').end(function (error, res) {
// type of res is array of objects
this.setState({
rows: res
});
});
}
// call this method on button clicked
handleClick() {
this.refreshList();
}
render() {
return(
<div>
<button onClick={this.handleClick}>Refresh List</button>
<Table rows={this.state.rows}/>
</div>
);
}
when call refreshList() new feteched data doesn't render.
My table component is:
// Table component
export class Table extends Component {
constructor(props) {
super(props);
this.state = {
rows: props.rows
}
}
render() {
return (
<div>
{this.state.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
Thanks a lot for your help. How can I refresh list on click button?
Your table component never changes its state after the construction. You can fix it easily by updating the state from new props:
export class Table extends Component {
constructor(props) {
super(props);
this.state = {
rows: props.rows
}
}
componentWillReceiveProps(newProps) {
this.setState({
rows: newProps.rows
});
}
render() {
return (
<div>
{this.state.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
However, if your table component is so simple, you can make it stateless and use props directly without setState():
export class Table extends Component {
render() {
return (
<div>
{this.props.rows.map((row, i) => (
<div>{row.title}</div>
))}
</div>
)
}
}
Note there is no need for constructor now. We could actually make it a functional component.
Use an arrow function:
req.get('/data').end((error, res)=> {
// type of res is array of objects
this.setState({
rows: res
});
});
With the ES5 style callback function, the context of this gets lost.
You could also bind this directly to a local variable, i.e., var that = this and stick with the function syntax, but I think most would agree what the ES6 arrow syntax is nicer.

TypeError: undefined is not an object - reactjs

I am running some tests on components for a small project, and I keep getting the same error for the one component. When I run the project, everything operates as intended, however when I test I can't get rid of this TypeError: undefined is not an object(this.props.searchResults.map). I am confused by this because, as I said, it runs fine. Is it a problem with the way I'm writing my tests or is there an error in my code? The component is below:
class Results extends React.Component {
render(){
const { handleEvent, searchResults } = this.props;
return(
<ul className="the-list">
{this.props.searchResults.map((result, idx) =>
<ResultItem
key={`${result.trackId}-${idx}`}
trackName={result.trackName}
track={result}
handleClick={handleEvent} />
)};
</ul>
);
}
}
It seems like this.props.searchResults is undefined in your test. There are two options here:
1: Define a default prop for searchResults:
Results.defaultProps = {
searchResults: []
}
2: Define searchResults in your test:
<Results searchResults={[]} />
I think it could be because you forget the constructor
class Results extends React.Component {
constructor(props) {
super(props);
}
render(){
const { handleEvent, searchResults } = this.props;
return(
<ul className="the-list">
{this.props.searchResults.map((result, idx) =>
<ResultItem
key={`${result.trackId}-${idx}`}
trackName={result.trackName}
track={result}
handleClick={handleEvent} />
)};
</ul>
);
}
}

Reactjs this.setState is not a function error

Im novice to React js, i don't know whats wrong with below code, but i'm getting setState is not a function error.Please help me to fix this.
class AppBarLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
visibleSideBar:true,
slide:""
}
}
showProfile(){
this.setState({
slide:'slide'
});
console.log(this.state.slide);
}
render(){
return(
<div>
<header>
<NavBar show={this.showProfile}/>
<Profile slide={this.state.slide} />
</header>
</div>
);
}
}
export default AppBarLayout;
You need to bind this.showProfile in the component constructor
this.showProfile = this.showProfile.bind(this)
More detail about this on the Handling Events page of the React doc : https://facebook.github.io/react/docs/handling-events.html
Expanding on Delapouite's answer if you don't like to bind every function in the constructor you can use arrow functions to automatically bind to the correct context.
For example:
class AppBarLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
visibleSideBar:true,
slide:""
}
}
// Now showProfile is an arrow function
showProfile = () => {
this.setState({
slide:'slide'
});
console.log(this.state.slide);
}
render(){
return(
<div>
<header>
<NavBar show={this.showProfile}/>
<Profile slide={this.state.slide}/>
</header>
</div>
);
}
}
export default AppBarLayout;
In my case, I solved the problem without binding.
Declaring the method like this was generating the error:
async onSubmit(e) {
event.preventDefault();
this.setState({ shopEthereumAddress: e.target.id });
}
The CORRECT declaration which will not generate the error is this:
onSubmit = async event => {
event.preventDefault();
this.setState({ shopEthereumAddress: event.target.id });
}
This works.
toggleSwitch() {
this.setState({
name: 'Ram ji'
});
}
Using an arrow function keeps the context of this set to the parent scope. The main benifit of arrow functions apart from being more concise is
Main benefit: No binding of ‘this’
// use this arrow function instead of
toggleSwitch = () => {
this.setState({
name: 'Ram ji' //It's working
});
}

Resources