React Native this.state.questions.map() is not a function - javascript

It's my understanding that the most common use care for iterating over a list of data is map, which is an array method that iterates over an array, but when I tried to apply it here:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import axios from 'axios';
class QuestionList extends Component {
state = { questions: [] };
componentWillMount() {
axios
.get('https://opentdb.com/api.php?amount=10&difficulty=hard&type=boolean')
.then(response => this.setState({ questions: response.data }));
}
// renderQuestions() {
// return this.state.questions.map(question => <Text>{}</Text>);
// }
render() {
console.log(this.state);
return (
<View>
<Text>{}</Text>
</View>
);
}
}
export default QuestionList;
I ended up getting an error in the Simulator saying that this.state.questions.map() is not a function. I have searched for similar errors online, but they do not apply to my use case.
Keep in mind I commented out the code and erased what I had inside of <Text> because my machine was about to take off.
I don't know what this error means short of not being able to use the map() array helper method, does that mean I need to be applying a different helper method to iterate through this list of questions?
I did a console log of the response object like so:
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import axios from 'axios';
class QuestionList extends Component {
state = { questions: [] };
componentWillMount() {
axios
.get('https://opentdb.com/api.php?amount=10&difficulty=hard&type=boolean')
.then(response => console.log(response));
}
render() {
console.log(this.state);
return (
<View>
<Text>{}</Text>
</View>
);
}
}
export default QuestionList;
and I got back the response object in the console:
from axios with a status of 200 which means the request was successful. You will notice I also go the data property and inside that is the results property and then the category with questions is inside of it:
So I am wondering if its that results property that I need to also implmement, but when I tried it I would get map() undefined.

Your API returns an object, which has no map method.
response.data.results is an array so change it to that if you intend to map over it:
this.setState({ questions: response.data.results }))
It's advisable to use componentDidMount instead of componentWillMount for async update.

Related

Get value from fetch in React

I try to pass an array of object from localhost:5000/users to Table component as a prop but I can't.
I can fetch data from localhost:5000/users and when I try to do console.log inside it, I can see data. But when I try to do console.log outside fetch function, it returns an empty array.
The question is how can I pass the data to Table component if the data is not visible outside the fetch function ?
import React from 'react';
import './App.css';
import Table from './Table';
function App() {
let obj = [];
fetch('http://localhost:5000/users')
.then((response) => {
return response.json();
})
.then((data) => {
return obj = data;
})
.then(() => {
console.log(obj); // Here it returns correct data from localhost:5000/users
return obj;
});
console.log(obj); // But right here, it returns an empty array
return (
<div>
<Table data={obj} /> {/* The question is how can I pass data from localhost:5000/users to Table component ? */}
</div>
)
}
export default App;
You need to use state and useEffect state in React.js .
I would recommend to invest more time on useState and useEffect. To do so React.js official documentation is good source to study. Here is also some resource links: how to use useState
how to use useEffect
import React, {useState} from 'react';
import './App.css';
import Table from './Table';
function App() {
const [obj, setObj] = useState([])
useEffect(() => {
fetch("http://localhost:5000/users")
.then((response) => {
return response.json();
})
.then((data) => {
//return obj = data;
setObj(data); // setting obj using setObj
})
.then(() => {
console.log(obj); // Here it returns correct data from localhost:5000/users
return obj;
});
}, []);
console.log(obj); // But right here, it returns an empty array
return (
{/* The question is how can I pass data from localhost:5000/users to Table component ? */}
)
}
export default App;
A solution can be : Create a state inside a constructor in your class.
Now when you fetch, setState the data inside your state :)
Now if you create a function outside your fetch it can be like this
onClick = () => {
console.log(this.state.data)
}
Now, you can do what you want with your data on all your component :)
And if you want to use the same component for many data, your state need to be an array, and you need to map your state :)
Have fun
I think this is happening because the fetch API call is a promise, therefore, the second console.log console.log(obj); // But right here, it returns an empty array runs before the promise resolves.
You can use state and useEffect as mentioned by Rahul Amin. I have created a js fiddle you can checkout. here. https://jsfiddle.net/titbetegya/owk7eg2a/18/

Rendering redux props map is not a function

I'm trying to render out a redux state by mapping through an array of objects but I'm getting map is not a function. I can console.log my props to see it is receiving but it looks as though it's trying to map through it before the props have been passed into the component. As you can see I've tried also using the && method as others have suggested but all I get back is:
TypeError: myItems.map is not a function
Here's the code I have
import React, {Component} from 'react';
import { connect } from 'react-redux';
class RandomComponent extends Component {
state = {
myItems: this.props.myItems
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('Styles: ', this.props.myItems); // Returns object array
}
render() {
const {myItems} = this.props; // also tried this.state
return (
<ul>
{myItems && myItems.map((item) => {
return <span>Hello.</span>
})}
</ul>
);
}
}
const mapStateToProps = state => ({
myItems: state.getmyItems.myItems
});
export default connect(mapStateToProps)(RandomComponent);
Your initialState is an object, set it to an empty array []. In your catch return an empty array and not an empty object. The && does not work because an empty object "exists". If u still want to use the && then set initialState to undefined
map is a function for arrays your data type might be an object. To iterate over an object you can use for ... in

Cannot render value of props from redux state

I have a redux state which is working fine in that my data is in the redux store. I can console.log it out with console.log(this.props) but I can't seem to render it out. It returns this error:
TypeError: Cannot read property 'somevalue' of undefined
Ususally I would map over props with a static block of html/jsx but I need this to be different html per loop so I'm trying to insert the values directly into the markup. Can anyone point me in the right direction please?
import React, {Component} from 'react';
import { connect } from 'react-redux';
class UnitClass extends Component {
state = {
unit: {}
}
render() {
console.log('Props is:', this.props); // shows correct values
return (
<ul>
<li>{this.props.unit.somevalue.value1}</li>
<li>{this.props.unit.somevalue.value2}</li>
<li>{this.props.unit.somevalue.value3}</li>
<li>{this.props.unit.somevalue.value4.someothervalue}</li>
</ul>
);
}
}
const mapStateToProps = state => ({
unit: state.setUnit.unit,
});
export default connect(mapStateToProps)(UnitClass);
try something like this
render() {
console.log('Props is:', this.props); // shows correct values
const {unit} = this.props;
return (
<ul>
{unit.somevalue && unit.somevalue.map((value)=>{
return value.someothervalue? <li>{value.someothervalue}</li>: <li>{value}</li>})
}
</ul>
);
}
note: for conditional rendering, you can use ternary operator and if you have to deal with nested conditional rendering, then I would recommend Higher Order Components, and then probably this

Rendering fetched data to screen

I built a component which uses Axios get request and retrieves a list of email addresses.
I don't know what should I write inside render() so I will be able to see the emails list over my website.
This is my suggestion:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
componentDidMount(){
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<button type = "button" onClick= {this.state.emails.map(email => <div>{email}</div>)}>GET ALL EMAILS</button>
</div>
);
}
}
When I check the Console I see an array of the desired emails.
I am looking for a suggestion of how to edit my code so it will render all this mails to the screen (After the button clicked).
Thanks is advance.
Inside your render method, you can map over this.state.emails and return an element for each email (At first the array will be empty, so maybe you can add a condition so you wouldn't render an empty div for no reason) Something like:
render() {
return (
<div>
{this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
As for componentDidMount - It's a lifecycle method of React. Anything you put there will run after the component mounts for the first time. If you want to trigger the call to Axios once the button is clicked, define a different function (like fetchEmails) and call it using this.fetchEmails.
You have used a componentDidMount life cycle in react to fetch the data. And you called that method via a button. Normally we are not calling life cycle methods like this. i think its better to read the react documentation doc for get an idea about life cycles.
You can declare a function and can call that function via a button. Please find below answer.
class App extends Component {
constructor(props) {
super(props);
this.state = {
emails: [],
showEmails:false,
};
}
componentDidMount () {
axios
.get("./api/EmailAddresses")
.then(response => {
this.setState({ emails: response.data });
console.log(response.data);
})
.catch(function(error) {
console.log(error);
});
}
render() {
return (
<div>
<button type="button" onClick={() => this.setState({showEmail:true})}>
Get all mails
</button>
{this.state.showEmail && this.state.emails.map(email => <div>{email}</div>)}
</div>
);
}
}
Change your code to something like below.
You need to get emails when button is clicked so you need have custom event handler function for that but not componentDidMount method. You cannot call componentDidMount method as event handler function.
Also when you render emails in loop you need to set unique key to top element inside loop. Key can be a index or unique id from data. Looks like you don’t have unique id from emails array so you can use index as key like below
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link} from "react-router";
import axios from 'axios';
export class GetEmailsComponent extends Component {
state = {
emails: []
}
getEmails = () =>{
//this.setState({emailList : undefined});
axios.get('./api/EmailAddresses')
.then(response => {
this.setState({emails: response.data});
console.log(response.data);
}).catch(function (error) {
console.log(error);
});
}
render() {
return (
<div>
<ul>
{this.state.emails.map((email, index)=> <li key={"Key-"+index}>{email}</li>)}
</ul>
<button type="button" onClick={()=> this.getEmails()}>Get all mails</button>
</div>
)
}
}

React redux - can't pull data from object in component props by key

I have a react component. It recieves questions - array of objects via reducer and getQuestions action.
There is also currentQuestionNumber - an integer, zero by default.
I have questions array in my state. I've mapped state to props.
In my render method I pull questions and currentQuestionNumber from props.
Inside return of my rendermethod I can do {console.log(questions[currentQuestionNumber])} - it logs the first object (index 0) in the array as expected.
A question object looks like this
{id: 1, text: "question 1", options: Array(4), correct: "1"}
But for some reason I can't do {console.log(questions[currentQuestionNumber].text)}- it returns an error TypeError: Cannot read property 'text' of undefined.
How can I fix it and why is this happening ?
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { getQuestions } from "../actions/questionActions";
class Question extends Component {
componentDidMount() {
this.props.getQuestions();
}
render() {
let { questions, currentQuestionNumber } = this.props;
return (
<div>
{console.log(questions[currentQuestionNumber])}
{console.log(questions[currentQuestionNumber].text)}
</div>
);
}
}
Question.propTypes = {
questions: PropTypes.array.isRequired,
getQuestions: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
questions: state.questions.questions,
currentQuestionNumber: state.questions.currentQuestionNumber
});
export default connect(
mapStateToProps,
{ getQuestions }
)(Question);
The error happens because your getQuestions() function is asynchronous and in the first render there is not any questions[currentQuestionNumber]. Then its text is undefined here. So, why can you console.log questions[currentQuestionNumber]?
Probably your questions state is an empty array: []. In the first render if you look carefully it should log undefined without any error since there is an empty array. After getting questions your component is being re-rendered and you see the question.
But, this is different for your .text case. There is not any question at that time, hence when you want to reach a non-existence question's text, react fires this error.
For this kind of situations you should use conditional rendering.
render() {
let { questions, currentQuestionNumber } = this.props;
return (
<div>
{questions.length && console.log(questions[currentQuestionNumber].text)}
</div>
);
}

Categories