Data not displaying when doing an ajax request in React - reactjs

So I'm trying to mock an ajax call using a React's Container(purely to fetch the data and pass it along to its children), but I'm not getting anything.
Instead get the following error: TypeError: Cannot read property 'map' of undefined. Which basically tells me that the users is either empty or not yet defined, right?
Right now I'm following the following structure UserListContainer (fetches the data) => UserList Component (displays the data as a prop).
UserList Container
// Container responsible only to fetch User data
import React from 'react';
import UserList from '../../ui/components/users/userList.jsx';
export default class UserListContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
users: []
}
}
componentDidMount() {
$.ajax({
// url: "../../users/users.json",
url: "https://gist.githubusercontent.com/dantesolis/267a298f3d6ac524bc2a7d80960a16b5/raw/7929bb23f1757b85adcead4eed3023cd3c7453df/users.json",
dataType: "json",
success: function(users) {
this.setState({users: users});
}.bind(this)
});
}
render() {
// const usr = this.props.user;
// let usrs = users_mockup ? users_mockup : this.props.users;
return (
<UserList users={this.state.users} />
);
}
}
UserList Component
import React from 'react';
import User from '../users/user.jsx';
// import { Link } from 'react';
export default class UserList extends React.Component {
constructor(props) {
super(props);
}
renderUser({name, _id}) {
return <li>{_id}-{name}</li>
}
render() {
return (
<div className="main">
<ul>{this.props.users.map(renderUser)}</ul>
</div>
);
}
}

That is because users is undefined in the success callback. This can happen because the URL is not working correctly. After taking a look at it, it seems it returns javascript and not json. You should try removing the comment in the first line to make it json compliant.

Related

React componentDidMount does not show axios response

I have React component RandomQouteMachine which is supposed to get response from the provided URL and display it on the page. I don't see response displayed. The debug 'Did component mount ?' message is missing too..
import React, { Component } from 'react';
import axios from 'axios';
lass RandomQouteMachine extends Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}
componenetDidMount() {
console.log('Did component mount ?');
axios.get('https://api.icndb.com/jokes/random')
.then((res) => {
this.setState({data:res.value.joke});
})
}
render() {
return (
<div>
Here it is:
{this.state.data}
</div>
);
}
}
export default RandomQouteMachine;
Am I using componenetDidMount() correctly ? I can see only 'Here it is:' displayed on page
check your spelling.
componenetDidMount !== componentDidMount

componentWillMount fails to set the state

I have a parent component called Home.js which retrieves the data from an API and send the data as a property to it's child component DishItem.js that shows the dish details. I am trying to render the dish_name, quantity (that can be changed) and net_cost, and I am storing all of these in a state and setting their state for the initial render through componentWillMount() lifecycle method. But it fails to set the state and returns undefined.
Home.js
import React, { Component } from 'react';
import DishItem from './DishItem';
import $ from 'jquery';
export default class Home extends Component {
constructor() {
super();
this.state = {
dish: ''
}
}
getDishes() {
$.ajax({
url: 'http://testbed2.riktamtech.com/foody_buddy/public/api/dishes/dish/31816',
dataType: 'json',
cache: false,
success: function(response) {
this.setState({dish: response.data});
}.bind(this),
error: function(xhr, status, err) {
console.log(err);
}
});
}
componentWillMount() {
this.getDishes();
}
componentDidMount() {
this.getDishes();
}
render() {
return (
<div className="Home">
<DishItem dish={this.state.dish}/>
</div>
);
}
}
DishItem.js
import React, { Component } from 'react';
import Quantity from './Quantity';
import { browserHistory } from 'react-router';
import './style.css';
export default class DishItem extends Component {
constructor(props) {
super(props);
this.state = {
quantity: 1,
delivery: '',
total_cost: '',
net_cost: null,
counter: 1
}
}
componentWillMount() {
console.log(this.props.dish.net_cost); //undefined instead of some value
this.setState({net_cost: this.props.dish.net_cost});
console.log(this.state.net_cost); // null
}
handleChangeInQuantity(value) {
var net_cost = 0;
var total_cost = 0;
total_cost = value * this.props.dish.cost;
net_cost = value * this.props.dish.net_cost;
this.setState({quantity: value, net_cost: net_cost, total_cost: total_cost});
}
saveAndContinue(e) {
e.preventDefault();
}
render() {
let address = <div>{this.props.dish.address}<br/>{this.props.dish.landmark}<br/>{this.props.dish.locality}</div>
return (
<div>
<h3>{this.props.dish.name}<small>{this.props.dish.description}</small></h3>
Total- {this.state.net_cost}
<Quantity change_in_quantity={this.handleChangeInQuantity.bind(this)} />
<button className="btn btn-default" onClick={this.saveAndContinue.bind(this)}>Next</button>
</div>
);
}
}
componentWillMount() function is triggered only once, it does not wait to get the data from the server. you should use componentWillReceiveProps() in the DishItem component, and set the new state with the data sent from the Home component to it. This way whenever the DishItem receives new props from the Home component, it will update accordingly.
You should not use the componentWillMount method of a React component to do asynchronous data fetching. Remove your call to the getDishes method and leave it in componentDidMount. In the render method of your Home component, check if the state already has the dish data in the state. If it doesn't, don't render the DishItem component. You can put a "Please wait, loading" message or something like that instead.
When the page is loaded, React will render the component with the "loading " message and start loading the dish data asynchronously. When the data is done loading and the state is set, React will render the component again with the DishItem component.
Here's an updated version of your Home component:
import React, { Component } from 'react';
import DishItem from './DishItem';
import $ from 'jquery';
export default class Home extends Component {
constructor() {
super();
this.state = {
dish: null
};
}
getDishes() {
$.ajax({
url: 'http://testbed2.riktamtech.com/foody_buddy/public/api/dishes/dish/31816',
dataType: 'json',
cache: false,
success: function (response) {
this.setState({ dish: response.data });
}.bind(this),
error(xhr, status, err) {
console.log(err);
}
});
}
componentDidMount() {
this.getDishes();
}
render() {
return (
<div className="Home">
{
this.state.dish === null ?
<div>Please wait, loading…</div> :
<DishItem dish={this.state.dish} />
}
</div>
);
}
}
it seems that I can't add a comment so I will just send this since there are already many answers. Please don't give me minus one because of this :)
Even though it is in componentDidMount, once it fetches the data, it will re-render the DOM so it will look like it is the initial render. If the fetching is slow, you will see it blank for a moment and then renders to what you need it to be.
If you really really need to make it show up at the very first moment, I suggest that you fetch the data and save it inside Flux store or Redux store before redirecting to this page. For example, fetch the data and do the redirection inside then() or callback function.

Meteor React Komposer: Execute Component Constructor AFTER ready subscription

I have a React Component Post:
export class Post extends React.Component {
constructor(props) {
this.state = this.props;
}
...
}
, which I compose with
import { composeWithTracker } from 'react-komposer';
import { composeWithTracker } from 'react-komposer';
import Post from '../components/post.jsx';
function composer(props, onData) {
const subscription = Meteor.subscribe('post', props.postId);
if (subscription.ready()) {
const data = {
ready: true,
posts: Posts.findOne(props.postId).fetch()
}
onData(null, data);
} else {
onData(null, {ready: false});
}
}
export default composeWithTracker(composer)(Post);
. As given in the Post Component I want to put some properties in the state of the component, but the constructor will be executed before I get the data from the composer!
How do I wait until the data is send and then put my props into the state?
Isn't this what the React Kompose should do? BTW I am using Version 1.~ to get composeWithTracker.
You could use componentWillReceiveProps to get new properties and set as component state. This function will run whenever there are new properties passed to component:
export class Post extends React.Component {
// ...
componentWillReceiveProps(nextProps) {
this.setState({
...nextProps,
});
}
// ...
}

componentWillMount does not finish before render

I am facing some difficulties regarding to state in reactjs.
As far as I know componentWillMount is a place to load the data with ajax call before the components get rendered.
I have a simple simple project which populates stack of panels with a loaded data and show it on board. However the data from ajax call do not get set before rendering of the component and this leads to rendering of the board with an empty array. The follwoing is my complete source:
import React from "react";
export class Panel extends React.Component{
render() {
return (
<div>
<div className="row panel">
<div className="col-sm-12 header">sfsfsf</div>
<div className="col-sm-12 body">fsdfsfs</div>
<div className="col-sm-12 footer">fasfaf</div>
</div>
</div>
);
}
}
and Board class which is a root of the problem is as follows :
import React from "react";
import {Panel} from "./Panel";
export class Board extends React.Component{
constructor(props){
super();
this.state={news: []};
}
componentWillMount(){
this.state={news: []};
$.ajax({
url: "http://localhost:3003/json.txt",
dataType: 'json',
cache: false,
success: function(data) {
var arr=[];
for (var key in data) {
arr.push(data[key]);
console.log(data[key]);
}
this.state={news: arr};
}});
}
render() {
return (
<div>
{
this.state.news.map((item,i)=> <Panel key="i"/>)
}
</div>
);
}
}
Also the last class is index.js:
import React from "react";
import {render} from "react-dom";
import {Board} from "./component/Board";
class App extends React.Component{
render(){
return(
<div>
<Board/>
</div>
);
}
}
render(<App/>, document.getElementById('middle'));
So as you can see in the Board.js class I initialize my array in render function and then I use componentWillMount to fill the news array which I expect it to happen after componentWillMount is finished but in my case the array is empty when rendering happens. Any idea?
*********UPDATE***************
I also tried it with componentDidMount but it did not work and the same problem
componentWillMount() is finishing before render but because ajax is async it will not execute until the request completes.
You should not set state using this.state = .... Instead use:
this.setState({news: arr});
This will set the value and trigger the component and all children to render. Just write your render function to handle null data nicely and you'll have your expected result without blocking JS execution.
As suggested here it is with proper binding:
import React from "react";
import {Panel} from "./Panel";
export class Board extends React.Component{
constructor(props){
super();
}
getInitialState() {
return {news: []};
}
componentWillMount(){
$.ajax({
url: "http://localhost:3003/json.txt",
dataType: 'json',
cache: false,
success: (data) => {
var arr=[];
for (var key in data) {
arr.push(data[key]);
console.log(data[key]);
}
this.setState({news: arr});
}
});
}
render() {
return (
<div>
{
this.state.news.map((item,i)=> <Panel key="i"/>)
}
</div>
);
}
}
The arrow function handles the binding. Simmilar to function(){}.bind(this)
You are doing a ajax call which is async... By definition it will continue to execute the code without waiting for the ajax's response.
you can turn that synchronous by setting async: false on the $.ajax options.

Setting component state, but this.props.state is undefined in render() method

I'm making a get request within my component's componentDidMount() method. I'm able to successfully make the request and set my component's state. However, when I try to get access to state within my render() method, it comes back as undefined. I'm guessing it has something to do with the asynchronous nature of javascript, but can't seem to figure out how to properly set the state, wait to make sure that state has been set, then pass it down to my render() method so I can access it there. Thanks.
Game.js (component file)
import React, { Component } from 'react';
import { Link } from 'react-router';
import axios from 'axios';
export default class Game extends Component {
constructor(props) {
super(props)
this.state = {
data: []
};
}
getReviews() {
const _this = this;
axios.get('/api/reviews')
.then(function(response) {
_this.setState({
data: response.data
});
console.log(_this.state.data); // shows that this is an array of objects.
})
.catch(function(response) {
console.log(response);
});
}
componentDidMount() {
this.getReviews();
}
render() {
const allReviews = this.props.data.map((review) => {
return (
<li>{review.username}</li>
);
})
console.log(this.props.data); // comes in as undefined here.
return (
<div>
{allReviews}
</div>
);
}
}

Resources