ReactJS - setState Axios POST response - javascript

I'm trying to set a state for my Axios POST response but the array where I map the data is still empty. The data fetching is working good, but I just need to move all the data with the 'subject' keywords to my Todos array and print them out.
Here is my code so far;
App.js
import React, { Component } from 'react';
import axios from 'axios';
import Todos from "./Components/Todos"
class App extends Component {
constructor(){
super();
this.state = {
todos:[]
}
}
// AXIOS request
getTodos(){
var postData = {
"startDate": "startDate",
"endDate": "endDate",
"studentGroup": ["ID"]
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic " + btoa("username" + ":" + "password")
}
};
axios.post('url', postData, axiosConfig)
.then((data) => {
console.log(data);
this.setState({todos: data.reservations ? data.reservations : []}, function(){
console.log(this.state);
})
})
.catch((err) => {
console.log("Error: ", err);
})
}
componentWillMount(){
this.getTodos();
}
componentDidMount(){
this.getTodos();
}
render() {
return (
<div className="App">
<Todos todos={this.state.todos}/>
</div>
);
}
}
export default App;
Todos.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';
class Todos extends Component {
render() {
let todoItems;
if(this.props.todos){
todoItems = this.props.todos.map(todo => {
return (
<TodoItem key={todo.subject} todo={todo} />
);
});
}
return (
<div className="Todos">
<h3>Results:</h3>
{todoItems}
</div>
);
}
}
export default Todos;
TodoItem.js
import React, { Component } from 'react';
class TodoItem extends Component {
render() {
return (
<li className="Todo">
<strong>{this.props.todo.subject}</strong>
</li>
);
}
}
export default TodoItem;
Snippet from console before setState:
Should I use some other function instead of an arrow function?

if you don't get this.setState is undefined error then it's a bit strange. Could you fix/copy the code below and verify if that helps:
import React, { Component } from 'react';
import axios from 'axios';
import Todos from "./Components/Todos"
class App extends Component {
constructor(props){
super(props); // pass props to "father" constructor
this.state = {
todos:[]
}
this.getTodos = this.getTodos.bind(this);
}
// AXIOS request
getTodos(){
var postData = {
"startDate": "startDate",
"endDate": "endDate",
"studentGroup": ["ID"]
};
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic " + btoa("username" + ":" + "password")
}
};
axios.post('url', postData, axiosConfig)
.then((response) => {
if(response.data.reservations) {
this.setState({todos: response.data.reservations})
}
})
.catch((err) => {
console.log("Error: ", err);
})
}
componentDidMount(){
this.getTodos();
}
render() {
console.log(this.state.todos);
return (
<div className="App">
<Todos todos={this.state.todos}/>
</div>
);
}
}
export default App;
Now observe if console.log(this.state.todos); is called after the request is finished. If so, verify it's an empty array.

Create self reference in function
getTodos(){
var self = this; // create a this reference
var postData = { .. };
let axiosConfig = {... };
axios.post(..)
.then((data) => {
self.setState('your updated state')
})
.catch((err) => {});
}

I was having hard time with the "this" undefined. I solved it like this (calling function should be arrow function):
handleGenerateCharge =(event) => {
const header = new Headers();
header.append('Access-Control-Allow-Origin', '*');
var charge = {
name: "ABC",
createDate: Moment(Date.now()).format()
}
axios.post("http://url", charge, header)
.then((res) => {
if (res.status === 200) {
this.update();
}
}).catch((error) => {
this.setState({ errorMessage: error });
})
event.preventDefault();
}

Related

Failed to Compile: 'movies' is not defined (React)

I'm getting this error message in a react app when I'm attempting to search for a movie and return a response of an array of movies.
src/App.js
Line 42:9: 'movies' is not defined no-undef
Line 42:19: 'movies' is not defined no-undef
Here is src/App.js:
import React from 'react';
import './App.css';
import unirest from 'unirest';
import Movie from './movie';
import Search from './search';
class App extends React.Component {
state = {
movies: []
}
sendRequest = (title) => {
const req = unirest("GET", "https://movie-database-imdb-alternative.p.rapidapi.com/");
req.query({
"page": "1",
"r": "json",
"s": title
});
req.headers({
'x-rapidapi-host': 'movie-database-imdb-alternative.p.rapidapi.com',
'x-rapidapi-key': 'my_api_key'
});
req.end((res) => {
if (res.error) throw new Error(res.error);
const movies = res.body.Search;
this.setState({movies});
console.log(res.body);
});
}
render() {
return (
<div className="App">
<header className="App-header">
{
movies && movies.length ? this.state.movies.map((movie) => {
return <Movie {...movie}/>
})
: null
}
<Search handleSendRequest={this.sendRequest}/>
</header>
</div>
);
}
}
export default App;
I'm not sure why it's saying 'movies' is undefined, I've set it as part of state in the beginning of the class itself. It seems like the req.end function is not accessing the state object for some reason?
Either you have to spread the state in render or use this.state.movies.
Using Destructuring
import React from 'react';
import './App.css';
import unirest from 'unirest';
import Movie from './movie';
import Search from './search';
class App extends React.Component {
state = {
movies: []
}
sendRequest = (title) => {
const req = unirest("GET", "https://movie-database-imdb-alternative.p.rapidapi.com/");
req.query({
"page": "1",
"r": "json",
"s": title
});
req.headers({
'x-rapidapi-host': 'movie-database-imdb-alternative.p.rapidapi.com',
'x-rapidapi-key': 'my_api_key'
});
req.end((res) => {
if (res.error) throw new Error(res.error);
const movies = res.body.Search;
this.setState({movies});
console.log(res.body);
});
}
render() {
const {movies} = this.state; // Use Destructuring
return (
<div className="App">
<header className="App-header">
{
movies && movies.length ? this.state.movies.map((movie) => {
return <Movie {...movie}/>
})
: null
}
<Search handleSendRequest={this.sendRequest}/>
</header>
</div>
);
}
}
export default App;
Or access state
import React from 'react';
import './App.css';
import unirest from 'unirest';
import Movie from './movie';
import Search from './search';
class App extends React.Component {
state = {
movies: []
}
sendRequest = (title) => {
const req = unirest("GET", "https://movie-database-imdb-alternative.p.rapidapi.com/");
req.query({
"page": "1",
"r": "json",
"s": title
});
req.headers({
'x-rapidapi-host': 'movie-database-imdb-alternative.p.rapidapi.com',
'x-rapidapi-key': 'my_api_key'
});
req.end((res) => {
if (res.error) throw new Error(res.error);
const movies = res.body.Search;
this.setState({movies});
console.log(res.body);
});
}
render() {
return (
<div className="App">
<header className="App-header">
{ // Use this.state to access movies.
this.state.movies && this.state.movies.length ? this.state.movies.map((movie) => {
return <Movie {...movie}/>
})
: null
}
<Search handleSendRequest={this.sendRequest}/>
</header>
</div>
);
}
}
export default App;
EDIT: Fixing res.body undefined.
import React from 'react';
import './App.css';
import unirest from 'unirest';
import Movie from './movie';
import Search from './search';
class App extends React.Component {
state = {
movies: []
}
sendRequest = (title) => {
const req = unirest("GET", "https://movie-database-imdb-alternative.p.rapidapi.com/");
req.query({
"page": "1",
"r": "json",
"s": title
});
req.headers({
'x-rapidapi-host': 'movie-database-imdb-alternative.p.rapidapi.com',
'x-rapidapi-key': 'my_api_key'
});
req.end((res) => {
if (res.error) throw new Error(res.error);
if(res && res.body && res.body.Search) {
const movies = res.body.Search;
this.setState({movies});
}
console.log(res.body);
});
}
render() {
return (
<div className="App">
<header className="App-header">
{
this.state.movies && this.state.movies.length ? this.state.movies.map((movie) => {
return <Movie {...movie}/>
})
: null
}
<Search handleSendRequest={this.sendRequest}/>
</header>
</div>
);
}
}
export default App;

React cant perform action on unmounted component due to status code check

I have been trying to setup a redirect if a query returns a 400 status {order not found} or if the query doesn't return anything. However, as soon as I add the status code check it gives this error:
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in OrderTracking (created by Context.Consumer)
import React, { Component } from 'react'
import TrackingRender from '../TrackingRender/TrackingRender'
export class OrderTracking extends Component {
constructor() {
super()
this.state = {
order: []
}
}
async componentDidMount() {
this._isMounted = true;
const { id } = this.props.match.params
const response = await fetch(`http://localhost:5000/api/orders/${id}`)
const data = await response.json()
if (!data || response.status == 400) {
await this.props.history.push('/orders')
}
this.setState({
order: data
})
console.log(this.state)
}
render() {
return (
<div>
< TrackingRender order={this.state.order} />
</div>
)
}
}
export default OrderTracking
Never mind! Used axios and reread some documentation on the error. Here is the updated code if anybody is interested or has the same error in the future.
import React, { Component } from 'react'
import TrackingRender from '../TrackingRender/TrackingRender'
import axios from 'axios'
import { Redirect } from 'react-router';
export class OrderTracking extends Component {
constructor() {
super()
this.state = {
loadingData: true,
order: []
}
}
async componentDidMount() {
this._isMounted = true;
if (this.state.loadingData) {
try {
const { id } = this.props.match.params
const response = await axios.get(`http://localhost:5000/api/orders/${id}`)
const data = await response.json()
this.setState({
order: data,
loadingData: false
})
console.log(this.state)
} catch (err) {
console.log(err)
}
}
}
render() {
if (!this.state.loadingData) {
return (
<div>
< TrackingRender order={this.state.order} />
</div>
)
} else {
return (
<Redirect to='/orders' />
)
}
}
}
export default OrderTracking
import React, { Component } from 'react';
import TrackingRender from '../TrackingRender/TrackingRender';
export class OrderTracking extends Component {
constructor() {
super();
this.state = {
order: [],
};
}
componentDidMount() {
var _this = this;
this._isMounted = true;
const { id } = this.props.match.params;
fetch(`http://localhost:5000/api/orders/${id}`)
.then((response) => {
if (response.ok) {
_this.setState({
order: data,
});
} else {
if (res.status === 400) {
this.props.history.push('/orders');
}
}
})
.then((data) => console.log('data is', data))
.catch((error) => console.log('error is', error));
}
render() {
return (
<div>
<TrackingRender order={this.state.order} />
</div>
);
}
}
export default OrderTracking;

Fetching data from api in componentDidMount is returning null

I am trying to fetch data in componentDidMount lifecycle method of react but I am not getting it.
my method is:
componentDidMount() {
const { taskId } = this.props
getTask(taskId)
.then(data => {
console.log(data);
this.setState({task: data});
})
}
my api is:
export const getTask = (unique_id) => {
console.log(unique_id)
return fetch('https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/'+ unique_id).then(res => {
return res.json();
});
};
this is my whole component:
import React, { Component } from 'react'
import { getTask } from '../../modules/clients';
import ClientTaskShow from '../../components/tasks/ClientTaskShow'
class ClientTaskShowContainer extends Component {
constructor(props) {
super(props)
this.state = {
messageModalOpen: false,
selectedPartnerId: null,
task:{}
}
}
componentDidMount() {
console.log("hello")
const { taskId } = this.props
getTask(taskId)
.then(data => {
console.log(data);
this.setState({task: data});
})
}
render() {
const taskSelected = this.state.task;
console.log(taskSelected)
return (
<ClientTaskShow
task={taskSelected}
/>
)
}
}
export default ClientTaskShowContainer;
code from where calling clienttaskShowContainer:
import React from 'react'
import Head from 'next/head'
import Layout from '../../components/Layout'
import ClientTaskShowContainer from '../../containers/tasks/ClientTaskShowContainer'
import requireAuth from '../../lib/requireAuth'
const ClientTasksShow = ({ query }) => {
const { taskId } = query
return (
<Layout fluid fullHeight clientTaskHeader='true'>
<Head>
<title>Client Task Details | Punctual</title>
</Head>
<ClientTaskShowContainer taskId={taskId} />
</Layout>
)
}
ClientTasksShow.getInitialProps = async ({ query }) => ({
query
})
export default requireAuth(ClientTasksShow)
I think its not hitting the API even. Although it hit once I restart the server but not again. I am not able to replicate the problem.
At some sites I found we should use .then for API call others says we can't pass perimeter in API call in componentDidMount. What is the exact solution for this. Please help. Thanks in advance.
This code is working
//Calling component
import React from "react";
import CallComp from "./CallComp";
import ReactDOM from "react-dom";
function App() {
return (
<div className="App">
<CallComp taskId={"7693fbf81a33"} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// Child Component
import React, { Component } from "react";
import ClientTaskShow from "./ClientTaskShow";
class ClientTaskShowContainer extends Component {
constructor(props) {
super(props);
this.state = {
task: {}
};
}
componentDidMount() {
const { taskId } = this.props;
fetch(
`https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/${taskId}`
)
.then(response => response.json())
.then(data => this.setState({ task: data }))
.catch(error => console.log("the error is", error));
}
render() {
const taskSelected = this.state.task;
console.log("task selected is ", taskSelected);
return (
<div>
{Object.keys(taskSelected).length ? (
<ClientTaskShow task={taskSelected} />
) : (
<div>No data to show</div>
)}
</div>
);
}
}
export default ClientTaskShowContainer;
// Demo ClientTaskShow
import React from "react";
const ClientTaskShow = ({ task }) => {
return <h1>{task.unique_code}</h1>;
};
export default ClientTaskShow;
Actually its working
console.log(data) returns error message from api
You should return promise from function to know api request is resolved or not
try this:
export const getTask = (id) => {
return new Promise(function (resolve, reject) {
fetch('https://punctual-backend-staging.herokuapp.com/api/v1/homeowner_tasks/' + id).then((res) => {
resolve(res.json())
})
});
}
And call like this:
componentDidMount() {
getTask(1).then((data)=>{
console.log(data);
});
}
You can replace params with your id
Hope this helps.

Unable to fetch data and React is not giving any error messages

I have this component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { compose } from 'redux';
import { translate } from 'react-i18next';
const API = `http://accountmanagement.ng.bluemix.net/v1/accounts/${accountGuid}/traits`;
class GetMFAValidation extends Component {
constructor(props) {
super(props);
this.state = { data: '' };
}
render() {
return (
<div>
<p>HOLA, I AM ${accountGuid}</p>
</div>
);
}
componentDidMount() {
fetch(API).then(response => response.json()).then(data => {
console.log('data -->', data);
this.setState({ data });
});
}
}
GetMFAValidation.propTypes = {
accountGuid: PropTypes.string.isRequired,
};
export default compose(
connect(
store => ({ accountGuid: store.global.accountGuid }),
translate(),
),
)(GetMFAValidation);
And I am calling it in another component like:
import GetMFAValidation from "../path"
And <GetMFAValidation />
The app crashes but I am not receiving any errors.
Your API string has a variable in it:
const API = `http://accountmanagement.ng.bluemix.net/v1/accounts/${accountGuid}/traits`;
...but accountGuid is not accessible here.
You could use a function instead:
const getAPIUrl = (accountGuid) =>
`http://accountmanagement.ng.bluemix.net/v1/accounts/${accountGuid}/traits`;
And then pass in the accountGuid from props when used:
componentDidMount() {
const APIUrl = getAPIUrl(this.props.accountGuid)
fetch(APIUrl).then(response => response.json()).then(data => {
console.log('data -->', data);
this.setState({ data });
});
}
I hope this helps.
in your fetch methode, try to add a catch after then, and print the error, if it is because of the fetch, your program will still work, and you will get the error
fetch(API).then(response => response.json()).then(data => {
console.log('data -->', data);
this.setState({ data });
})
.catch(error => console.log(error));

Setting response from axios to state

When I try to setState after the get axios request it doesn't seem that I have the data ready to render.In console, I am getting a response but unable to access the response in the state.
import React, { Component } from 'react';
import axios from 'axios';
import { Grid, Row, Col } from 'react-flexbox-grid';
import Avatar from '#material-ui/core/Avatar';
import Button from '#material-ui/core/Button';
import stylefile from './stylefile';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import { withStyles } from '#material-ui/core/styles';
import '../App.css';
class TitleBar extends Component {
constructor() {
super();
this.state ={
data:[],
}
}
componentDidMount() {
axios.get('http://api.abc',
{
headers: { "Postman-Token": "abc"}
})
.then((response) => {
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
render() {
const { classes } = this.props;
console.log(this.state.data,"data response")
return (
<div>
{
this.state.data.map(((item,key) => (
<div>
//
</div>
)))}
</div>
);
}
}
export default withStyles(stylefile)(TitleBar);
console.log(error);
});
}
//console.log(this.state.data) -- is undefined
Your API response object includes an object like:
const response = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" }
],
HASH: "3--0"
}
};
So, you need response.data.DATA for your state here:
this.setState( { data: response.data.DATA } );
Here is a working example of mimicking your situation.
const remoteData = {
data: {
MESSAGE: "List details Fetch successful",
STATUS: "SUCCESS",
DATA: [
{ id: 1, name: "foo" },
{ id: 2, name: "bar" },
{ id: 3, name: "baz" },
],
HASH: "3--0",
},
};
const fakeRequest = () =>
new Promise( resolve => setTimeout( () => resolve( remoteData ), 1000 ) );
class App extends React.Component {
state = {
data: [],
};
componentDidMount() {
fakeRequest().then( response => this.setState( { data: response.data.DATA } ) );
}
render() {
return (
<div>
{this.state.data.map( el => (
<div key={el.id}>
<p>{el.id}</p>
<p>{el.name}</p>
</div>
) )}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById( "root" )
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
From React official docs,
componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering
Also,one should use componentDidMount as componentWillMount is deprecated in new version of react.
componentDidMount() {
axios.get('http://api./abc',
{
headers: { "Postman-Token": "abc" }
})
.then((response) => { //use arrow to get setState on this call without any extra binding or placeholder variable
console.log(response.data);
this.setState({
data: response.data,
})
})
.catch((error) => {
console.log(error)
})
}
Try to fix those lines of code:
constructor(props) { //fixed
super(props); //fixed
this.state ={
data:[],
}
}
This is just the way ReactJS set up the constructor() method for a class component. We just obey the React's rules while working with it.
From the Official React Document, they said:
The constructor for a React component is called before it is mounted. When implementing the constructor for a React.Component subclass, you should call super(props) before any other statement. Otherwise, this.props will be undefined in the constructor, which can lead to bugs.
For more information about the contructor() method: https://reactjs.org/docs/react-component.html#constructor
This is a working example which I've already made just for you as a reference.
The demo is now available on CodeSandBox: https://codesandbox.io/s/8xvn8yl1l2
TitleBar.js
import React, { Component } from 'react';
import axios from 'axios';
export default class TitleBar extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
axios
.get('https://jsonplaceholder.typicode.com/posts')
.then(res => {
console.log(res.data);
this.setState({ data: res.data });
})
.catch(err => console.log(err.message));
}
render() {
return (
<div>
{this.state.data.map(i => (
<div key={i.id}>
<h2>{i.title}</h2>
</div>
))}
</div>
);
}
}
App.js
import React, { Component } from 'react';
import TitleBar from './components/TitleBar';
class App extends Component {
render() {
return (
<div>
<TitleBar></TitleBar>
</div>
);
}
}
export default App;
By following this example, if the this.state.data is still undefine, then there are two things that we could focus on in order to successfully debug:
1.The structure of the response data object. In your case, the solution could be
this.setState({
data: response.data.DATA
})
2.Does the API work as expected.
Hopefully that helps.

Categories

Resources