I'm new to react and I'm trying to pull and display data from randomuserapi. I've made the api call but when I run my app, I get the error below:
./src/App.js
Line 45: 'getData' is not defined no-undef
Here's my code below: The getData() method is where I make the api call. That method is now called in ComponentDidMount.
I also binded getData() to my constructor but I still get the error above.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
people: []
}
this.getData = this.getData.bind(this);
}
getData() {
const promise = fetch('https://randomuser.me/api/?results=20')
.then(response => {
if (response.status >= 400) {
throw `Response Invalid ( ${response.status} )`;
return;
}
return response.json();
})
.then(({results}) => {
return results;
})
.catch(error => {
console.log(error);
});
return promise;
}
ComponenDidMount() {
getData()
.then(data => {
this.setState({
people: data
});
});
}
render() {
return (
<div>
<p>{this.state.people.results[0].gender}</p>
</div>
);
}
}
export default App;
I'm also using create-react-app from Github. Please some assistance will be helpful.
Thanks!
When you reference defined methods you need to say this so:
componenDidMount() {
this.getData()
.then(data => {
this.setState({
people: data
});
});
}
Try adding this. when calling your functions.
this.getData() inside componentDidMount
Related
I am getting the following error in my code. Can you please help me to understand the issue. I have included my page component and task list component.
TypeError: this.state.tasks.map is not a function
Page Show.js
import React, { Component } from 'react';
import axios from 'axios';
import TasksList from './TasksList';
export default class Show extends Component {
constructor(props) {
super(props);
this.state = {tasks: [] };
}
componentDidMount(){
axios.post('http://mohamed-bouhlel.com/p5/todolist/todophp/show.php')
.then(response => {
this.setState({ tasks: response.data });
})
.catch(function (error) {
console.log(error);
})
}
tasksList(){
return this.state.tasks.map(function(object,i){
return <TasksList obj = {object} key={i} />;
});
}
render() {
return (
<div>
{ this.tasksList() }
</div>
)
}
}
Page TasksList.js
import React, { Component } from 'react';
export default class TasksList extends Component {
render() {
return (
<div>
<div>{this.props.obj.task}</div>
</div>
)
}
}
Using a GET request and correct protocol (https vs http) seems to resolve the issue.
axios.get("https://mohamed-bouhlel.com/p5/todolist/todophp/show.php")
Response.data is not an array and basically you can't call map on a non-array.
I suggest console.log(response.data) to check the data type.
And I guess maybe you're doing a axios.post instead of a correct axios.get. log the response.data and you'll find out.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
In React ... I am trying to read the response return from API and get undefined, what is the problem?
Undefined occurs when calling the function retrieveItems() from the component.
**// item service class**
import axios_o from 'axios';
class ItemService {
retrieveItems() {
axios_o.get("https://jsonplaceholder.typicode.com/posts")
.then(response => {
return response;
}).catch();
}
}
**// component calling the item service**
import React from 'react'
import ItemService from "../Services/ItemService";
class Posts extends React.Component {
constructor(props) {
super(props);
}
componentDidMount = () => {
this.itemservice=new ItemService();
**console.log(this.itemservice.retrieveItems())**
}
render() {
return (
<h1>Posts List</h1>
);
}
}
export default Posts;
class ItemService {
retrieveItems() {
return axios_o.get("https://jsonplaceholder.typicode.com/posts")
.then(response => response)
.catch(error => error)
}
}
componentDidMount = () => {
this.itemservice=new ItemService();
this.itemservice.retrieveItems().then(res=>{
console.log(res);
}).catch(error=>{
console.log(error)
});
}
As I mentioned in the comment the method retrieveItems is not returning a value. To fix this return the axios call
retrieveItems() {
return axios_o.get("https://jsonplaceholder.typicode.com/posts")
.then(response => {
return response;
}).catch(
);
}
or rewrite it to async/await for better readability
async retrieveItems() {
try {
return await axios_o.get("https://jsonplaceholder.typicode.com/posts")
}catch(e) {
// do some error handling or move the try/catch to caller side
}
}
Now in your console log you should see not the real response of the API call but a Promise. To get the real response you also have to wait for the answer on caller side:
class Posts extends React.Component {
constructor(props) {
super(props);
}
componentDidMount = () => {
this.retrieveItems()
}
retrieveItems = async () => {
this.itemservice=new ItemService();
const response = await this.itemservice.retrieveItems()
console.log(response)
}
render() {
return (
<h1>Posts List</h1>
);
}
}
With this you should see the response in the console log.
The issue is the typical pitfall of wanting to return something from within a callback function to the outer function. That's can't work, because the outer function (retrieveItems) has already finished. You need to stay in the asynchronous pattern. The easiest is probably this:
import axios_o from 'axios';
class ItemService {
retrieveItems() {
return axios_o.get("https://jsonplaceholder.typicode.com/posts");
}
}
import React from 'react'
import ItemService from "../Services/ItemService";
class Posts extends React.Component {
componentDidMount = () => {
this.itemservice = new ItemService();
this.itemservice.retrieveItems().then((res) => {
console.log(res);
});
}
render() {
return (<h1>Posts List</h1>);
}
}
export default Posts;
I have set up an API with Rails, with a http://localhost:3001/api/words endpoint exposing the following data:
[{"id":1,"term":"Reach","definition":"Reach is the number of people who had any content from your Page or about your Page enter their screen.","example":"","author":"Loomly","credit":"https://www.loomly.com/","created_at":"2018-11-02T03:21:20.718Z","updated_at":"2018-11-02T03:21:20.718Z"},{"id":2,"term":"Emoji","definition":"A small digital image or icon used to express an idea, emotion, etc., in electronic communication","example":"","author":"Loomly","credit":"https://www.loomly.com/","created_at":"2018-11-02T03:23:50.595Z","updated_at":"2018-11-02T03:23:50.595Z"}]
I am now trying to simply display this data (ideally as an unordered list) in a React.js frontend application built with Create React App, and here is the content of my App.js file:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor () {
super()
this.state = {}
this.getWords = this.getWords.bind(this)
this.getWord = this.getWord.bind(this)
}
componentDidMount () {
this.getWords()
}
fetch (endpoint) {
return window.fetch(endpoint)
.then(response => response.json())
.catch(error => console.log(error))
}
getWords () {
this.fetch('/api/words')
.then(words => {
if (words.length) {
this.setState({words: words})
this.getWord(words[0].id)
} else {
this.setState({words: []})
}
})
}
getWord (id) {
this.fetch(`/api/words/${id}`)
.then(word => this.setState({word: word}))
}
render () {
let {words, word} = this.state
return (
<div>
{Object.keys(words).map((key) => {
return (
<div key={word.id}>
<p>{word.term}</p>;
</div>
)
})}
</div>
)
}
}
export default App;
I believe the problem is located in the following area of the code:
render () {
let {words, word} = this.state
return (
<div>
{Object.keys(words).map((key) => {
return (
<div key={word.id}>
<p>{word.term}</p>;
</div>
)
})}
</div>
)
}
I have tried to follow the steps explained in this tutorial, as well as in that other tutorial, while keeping the layout of the page as simple as possible (no bells & whistles from semantic-ui-css), and no matter what I try, I keep getting into of the following errors:
TypeError: Cannot convert undefined or null to object
Unexpected token, expected “,”
Failed to compile: 'word' is not defined no-undef
The solution explained in this article led me to the code I have now, but there is something I am missing about the way to structure my React app: can you point me in the right direction?
getWords () {
fetch('http://localhost:3001/api/words')
.then((response) => {
return response.json();
})
.then((res) => {
// console.log(res); you should get the response you mentioned
this.setState({words: res});
});
}
Then check Are you getting data in your state by consoling it.
Then you can work on it using following
render{
return(
<div>
{ this.state.words.map((val) => (
<span>{val.term}</span>
))}
</div>
)
}
The problem is here: let {words, word} = this.state;
this.state doesnt have word property yet. You could initialize this.state like this:
this.state = {
words: [],
word: {}
};
be free to ask
There are two issues with the code in the question:
words & word are not defined.
Iteration through words in the render() function was not set properly with keys.
Thanks to the answers and comments left on this question, here is the code I ended up using:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor () {
super()
this.state = {
words : [],
word : {}
}
this.getWords = this.getWords.bind(this)
this.getWord = this.getWord.bind(this)
}
componentDidMount () {
this.getWords()
}
fetch (endpoint) {
return window.fetch(endpoint)
.then(response => response.json())
.catch(error => console.log(error))
}
getWords () {
this.fetch('/api/words')
.then(words => {
if (words.length) {
this.setState({words: words})
this.getWord(words[0].id)
} else {
this.setState({words: []})
}
})
}
getWord (id) {
this.fetch(`/api/words/${id}`)
.then(word => this.setState({word: word}))
}
render () {
let {words} = this.state
return (
<ul className="words-container">
{Object.keys(words).map((key) => {
return (
<li className="word-container" key={key}>
{words[key].term}: {words[key].definition}.
</li>
)
})}
</ul>
)
}
}
export default App;
Could someone provide me with a little bit of guidance on my class object and how to reference it in another in my project?
Here is my RequestAPI object - request-api.js (note: I understand that there isn't much going on in it yet, but I wanted to walk before I can run)
export class RequestApi {
constructor() {
this.apiBase = '../api';
}
fetch(url, options) {
var options = options || {};
return fetch(this.apiBase + url, options)
.then(_handleResponse, _handleNetworkError);
}
_handleResponse(response) {
if (response.ok) {
return response.json();
} else {
return response.json().then(function (error) {
throw error;
});
}
}
_handleNetworkError(error) {
throw {
msg: error.message
};
}
}
Here is the React Class component that i am trying to reference it in:
import React from 'react';
import { RequestApi } from '../../../../utils/request-api.js';
class UserLayout extends React.Component {
constructor() {
super();
this.state = {
users: [],
isLoading: true
};
this.addNewUser = this.addNewUser.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
componentDidMount() {
return RequestApi.fetch('/user')
.then(json => {
this.setState({
isLoading: false,
users: json
});
})
.catch(error => {
console.error(error.msg);
});
}
// more code here...
}
I get an error in my React Component Class object: Uncaught TypeError: _requestApi.RequestApi.fetch is not a function
Can anyone provide me with some insight/assistance?
Since fetch is not a static method, you need to create an instance of RequestApi prior to calling fetch on it:
componentDidMount() {
const api = new RequestApi();
return api.fetch('/user')
.then(json => {
this.setState({
isLoading: false,
users: json
});
})
.catch(error => {
console.error(error.msg);
});
}
How can I pass data I receive from a get request pass over to a component? Whatever I tried wouldn't work but my thinking was as the code below shows..
Thanks!
export function data() {
axios.get('www.example.de')
.then(function(res) {
return res.data
})
.then(function(data) {
this.setState({
list: data
})
})
}
import {data} from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ""
};
}
componentWillMount() {
data();
}
render() {
return <p > this.state.list < /p>
}
}
You call this.setState inside of data()->then callback, so this is context of the then callback function. Instead you should use arrow functions (it does not have its own context) and pass component's this to data function using call
export function data() {
axios.get('www.example.de')
.then(res => res.data)
.then(data => {
this.setState({
list: data
})
})
}
import {data} from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
list: ""
};
}
componentWillMount() {
data.call(this);
}
render() {
return <p > this.state.list < /p>
}
}
However, your data services must not know about setState and, event more, expect passing this from react component. Your data service must be responsible for retrieving data from server, but not for changing component state, see Single responsibility principle. Also, your data service can be called from another data service. So your data service should return promise instead, that can be used by component for calling setState.
export function data() {
return axios.get('www.example.de')
.then(res => res.data)
}
and then
componentWillMount() {
data().then(data=>{
this.setState({
list: data
})
});
}
your api shouldn't know anything about your component, you can easily do this with callback, like so -
export function data(callback) {
axios.get('www.example.de')
.then(res => callback({ data: res.data }))
.catch(err => callback({ error: err }));
}
By doing this you can easily unit test your api
So in your Test component, you simply do -
componentWillMount() {
data(result => {
const { data, error } = result;
if (error) {
// Handle error
return;
}
if (data) {
this.setState({ list: data });
}
});
}
Your request is a promise so you can simply return that from the imported function and use the eventual returned result of that within the component. You only want to be changing the state of the component from within the component.
export function getData(endpoint) {
return axios.get(endpoint);
}
Note I've changed the name of the function to something more "actiony".
import { getData } from './api.js';
class Test extends React.Component {
constructor(props) {
super(props);
// Your state is going to be an array of things, so
// initialise it with an array to spare confusion
this.state = { list: [] };
}
// I use ComponentDidMount for fetch requests
// https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/
componentDidMount() {
// We've returned a promise from `getData` so we can still use the
// promise API to extract the JSON, and store the parsed object as the
// component state
getData('www.example.de')
.then(res => res.data)
.then(list => this.setState({ list }))
}
}
Your external function doesn't have the correct context of this, so you'll need to call it with the correct context from within the component:
componentWillMount() {
data.call(this);
}
However, inside the API call, it still won't have the correct this context, so you can set a variable to point to this inside the data() function:
export function data() {
let that = this;
axios('http://www.url.com')
.then(function(res) {
return res.data
})
.then(function(data) {
that.setState({
list: data
})
})
}
Details of the this keyword
However, it's generally considered better practice to only handle your state manipulation from with the component itself, but this will involve handling the asynchronous nature of the GET request, perhaps by passing in a callback to the data() function.
EDIT: Updated with asynchronous code
//api.js
data(callback){
axios.get('www.url.com')
.then(res => callback(res));
}
//component.jsx
componentWillMount(){
data(res => this.setState({list: res}));
}