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

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>
);
}
}

Related

React - can't call setState on a component that is not yet mounted (scaledrone app)

it gives me the same error no matter what i try;
its either that error or my push function breaks
full error is: "Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the App component."
import React, { Component } from 'react';
import './App.css';
import Messages from "./Messages";
import Input from "./Input";
class App extends Component {
constructor() {
super();
this.state = {
messages:[],
member: {
username: randomName(),
color: randomColor(),
},
}
this.drone = new window.Scaledrone("Qk3ma3HbEXr6Lwh7", {
data: this.state.member
});
this.drone.on('open', error => {
if (error) {
return console.error(error);
}
const member = {...this.state.member};
member.id = this.drone.clientId;
this.state.member = {...member};
});
const room = this.drone.subscribe("observable-room");
room.on('data', (data, member) => {
const mcopy = this.state.messages;
mcopy.push({member, text: data});
this.setState({mcopy});
});
}
render() {
return (
<div className="App">
<div className="App-header">
<h1>Chat Aplikacija</h1>
</div>
<Messages
messages={this.state.messages}
currentMember={this.state.member}
/>
<Input
onSendMessage={this.onSendMessage}
/>
</div>
);
}
onSendMessage = (message) => {
this.drone.publish({
room: "observable-room",
message
});
}
}
export default App;
You should not call setState() in the constructor(), Technically setState is meant to update existing state with a new value. you should move state manipulation to ComponentDidMount life cycle.
Also, don't mutate state, instead make a clone and then make changes.

Export a dynamic array from a React component to another component

I built a react component that imports a Json file into an array to map the result. I need that array in another component. I don't know if I must built this component inside the new component or if there's a method to export the needed array (data). The array source is updated every 4 seconds.
Thanks for your help.
My first component is:
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
class Ramas extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
componentDidMount() {
const fetchData = () => {
axios
.get('http://localhost:8888/dp_8/fuente/procesos_arbol.json')
.then(({ data })=> {
this.setState({
data: data
});
console.log(data);
})
.catch(()=> {console.log('no recibido');});
};
fetchData();
this.update = setInterval(fetchData, 4000);
} // final componentDidMount
render() {
const initialData = this.state.data.map((el) => {
return (
<p>id={ el.id } | name - { el.name } | padre - {el.parent}</p>
);
});
return (<div className="datos_iniciales">
{ initialData }
</div>);
}
}
ReactDOM.render(
<Ramas />,
document.getElementById('container')
);
make one top level component that can contain the two components.
in the Ramas component ->
const updatedData = setInterval(fetchData, 4000);
this.props.datasource(updatedData);
write a new top level component ->
class TopComponent Extends React.Component{
state = {data: ''}
handleDataUpdate = (updatedData) => {
this.setState({data: updatedData});
}
render = () => {
<Ramas datasource={this.handleDataUpdate}>
<SecondComponent updatedData={this.state.data}>
</Ramas>
}
}
now from SecondComponent updatedData prop you can get the fresh data
By the way it is in ES7 syntax I wrote
If you have parent component, you should pass function from it to this component as a prop.
That function will than set state and data will flow one way as it's imagined with ReactJS.
For example instead of this.setState, you could call
this.props.jsonToArray
and in jsonToArray you should call setState which will pass data to that seccond component.

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.

TypeError: cannot read property setState of undefined

I am receiving the data from server but not able to display it on the browser. I am receiving error as:
caught (in promise) TypeError: Cannot read property 'setState' of undefined
import React from 'react';
import axios from 'axios';
class App extends React.Component {
constructor(props){
super(props);
this.state={
posts:'hello',
dos:[]
}
};
componentDidMount() {
axios.get(`http://192.168.1.9:8082`)
.then(function(data) {
console.log(data);
this.setState({dos:data});
});
}
render() {
return (
<div>
Hello World!!!
<h1>{this.state.posts}</h1>
<h2>{this.state.dos}</h2>
</div>
);
}
}
export default App;
Its a context issue, your mistake is that you didn't bind all the way down to the anonymous function. What you probably want to do is use arrow functions, try this:
componentDidMount() {
axios.get(`http://192.168.1.9:8082`)
.then(data => this.setState({dos:data}););
}
Arrow functions always keep the context of this.
Alternate solution:
componentDidMount() {
axios.get(`http://192.168.1.9:8082`)
.then(function(data) {
console.log(data);
this.setState({dos:data});
}).bind(this);
}
try this one:
componentDidMount() {
let self = this;
axios.get(`http://192.168.1.9:8082`)
.then(function(data) {
console.log(data);
self.setState({dos:data});
});
}

Data not displaying when doing an ajax request in React

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.

Resources