beginner at react.js - rendering component from state which is updated later - reactjs

I'm trying to test rendering a list component from an JSON object called with fetch, however I suspect the render is called before the JSON is returned as I get an undefined error. Note the debug alert() gets the value, but the render has already error on undefined. How do I handle this? Is there a way to call the render function after the data has been fetched? OR could how would I get the data to render with '?' and then update when the data arrives? Note my if (!object) doesn;t work when called as an inline wrapper - why not - or more impoartanly how would I achive this? Sorry for the begginer questions - I suspect I meant to be using a data framework to bring data for react to display but I wanted to start learning with less frameworks and get the basics. Many thanks! - code is:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class ItemLister extends React.Component {
constructor(props ) {
super(props);
this.state = { items: [] };
}
componentDidMount() {
let url = "http://localhost:8888";
let iterator = fetch(url, {method: 'GET'});
iterator
.then(response => {
//console.log('sss', response)
return response.json();
})
.then(post => {
//console.log(post)
this.state.items = post;
alert(this.state.items.users[3].firstname);
});
}
output(object){
if (!object){
return '?';
}else{
return object;
}
}
render() {
return(
<div>
<div>Items:</div>
<div>{this.output(this.state.items.users[3].firstname)}</div>
</div>
);
}
}
export default ItemLister;

your render function could be something like this:
render() {
const username = (this.state.items === []) ? placeholder : this.output(this.state.items.users[3].firstname;
return(
<div>
<div>Items:</div>
<div>{username}</div>
</div>
);
}
basically, you render a placeholder component until your data arrives, since your data fetch is async and in componentDidMount, so your render will happen initially before you have data.
although you probably want to rethink how this component is constructed in general, why are you accessing specifically users[3]?

Try this code it will help you
import React, { Component } from "react";
import logo from "./logo.svg";
import "./App.css";
class ItemLister extends Component {
constructor(props) {
super(props);
this.state = { items: [], firstname: "" };
}
componentDidMount() {
let url = "http://localhost:8888";
let iterator = fetch(url, { method: "GET" });
iterator
.then(response => {
//console.log('sss', response)
return response.json();
})
.then(post => {
//console.log(post)
this.setState({
items: post,
firstname: this.state.items.users[3].firstname
});
alert(this.state.items.users[3].firstname);
});
}
output(object) {
if (!object) {
return "?";
} else {
return object;
}
}
render() {
return (
<div>
<div>Items:</div>
<div>{this.state.firstname}</div>
</div>
);
}
}
export default ItemLister;

Related

Update state with API Call - React.js

I'm trying to update state in React.js using an API call, as I need to show some of the data to the end user.
The api call works through localhost:5001 and is stored in Firebased functions.
import React, { Component } from 'react'
import './Table.css';
class Table extends Component {
constructor (props)
{
super(props);
this.state = {
stocks: []
};
}
componentDidMount() {
fetch('localhost:5001') // Removed for stackoverflow //
.then((response) => response.json())
.then(stockList => {
this.setState =
({ stocks: stockList });
});
}
render() {
return (
<div className='table'>
<h1 id='title'>Companies</h1>
{this.state.stocks.map(stocks => <h2 key={stocks.symbol}> {stocks.companyName}</h2>)}
</div>
)
}
}
export default Table;
Here is a snippet of the API call:
{"symbol":"AAPL","companyName":"Apple Inc"}
setState is a function, so you should call it, rather that assign values to it:
this.setState({ stocks: stockList });

React is not rendering if state is set in constructor by a function

I'm trying to initiate the page according to user login status. For this purpose I tried to set initial state by a function but it's not updating view after api call completed.
Currenty I see Part 3 when codes are executed.
import React, { Component } from 'react';
import axios from 'axios';
import { API_BASE_URL } from './constants/apiConstants';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
loginStatus: this.loginUpdate(),
}
}
loginUpdate(){
let token = localStorage.getItem("TOKEN");
let tmpStatus = token!==""?true:false;
if(tmpStatus){
const payload = {
"token": token
}
axios.post(API_BASE_URL + 'loginCheck', payload)
.then((response) => {
tmpStatus=response.data.response;
return tmpStatus;
});
}else{
return tmpStatus;
}
return null;
}
render() {
return (
<>
{this.state.loginStatus===true &&
<p>Part 1</p>
}
{this.state.loginStatus===false &&
<p>Part 2</p>
}
{this.state.loginStatus===null &&
<p>Part 3</p>
}
</>
)
}
}
Problem:
You should update the state variable inside the then callback of Axios. You have an API call in loginUpdate function but the problem is that your HTML renders first and then your API response comes. Also, you missed return statement before Axios but putting that will also not resolved your issue.
Solution:
You should set your state variable inside the then callback.
import React, { Component } from 'react';
import axios from 'axios';
import { API_BASE_URL } from './constants/apiConstants';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
loginStatus: localStorage.getItem("TOKEN") !== '' ? true : false,
}
}
componentDidMount() {
this.loginUpdate();
}
loginUpdate(){
let token = localStorage.getItem("TOKEN");
let tmpStatus = token!==""?true:false;
if(tmpStatus){
const payload = {
"token": token
}
axios.post(API_BASE_URL + 'loginCheck', payload)
.then((response) => {
this.setState({ loginStatus: response.data.response });
});
}else{
return tmpStatus;
}
return null;
}
You should return axios result as well.
return axios.post(API_BASE_URL + 'loginCheck', payload)
.then((response) => {
tmpStatus=response.data.response;
return tmpStatus;
});
I was able resolve the issue with the following approach:
import React, { Component } from 'react';
import axios from 'axios';
import { API_BASE_URL } from './constants/apiConstants';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
loginStatus: null,
}
}
componentDidMount() {
this.loginUpdate();
}
loginUpdate(){
let token = localStorage.getItem("TOKEN");
let tmpStatus = token!==""?true:false;
if(tmpStatus){
const payload = {
"token": token
}
axios.post(API_BASE_URL + 'giris/kontrol', payload)
.then((response) => {
tmpStatus=response.data.response;
this.setState({loginStatus:tmpStatus});
});
}else{
this.setState({loginStatus:tmpStatus});
}
}
render() {
return (
<>
{this.state.loginStatus===true &&
<p>Part 1</p>
}
{this.state.loginStatus===false &&
<p>Part 2</p>
}
{this.state.loginStatus===null &&
<p>Part 3</p>
}
</>
)
}
}
While constructor is running, "loginStatus" state is set to the first result of your method "loginUpdate()", which is "null". When the method "loginUpdate()" is called in constructor, it starts the axios, but not wait for the answer of axios. It will be running as async. After the "null" value is set, constructor is finished its work. So, the result of axios will never be used.
For this reason, you should use this.setState() function when axios result is taken. Also, you should not use the method in constructor. Therefore, if you put the method in "componentDidMount" and use setState function after the result is taken on axios, you will get the result expected. In setState function you should set "loginStatus" with axios response.

React HOC with Router not finding page

Hi I am working on a react app with Routing and HOC. I expect to see a page but i get page not found when i know the page is there.
in componentDidMount this.setState, data is shown as undefined but in the HOC wrapper i see the data arrive from the server.
Before I wrapped the page in HOC i could see it rendering content so I know the content exists.
Here is my Page component which is being called via a Route :
import React, { Component } from "react";
import WithBackend from "./WithBackend";
class Page extends Component {
constructor(props) {
super(props);
this.state = { model: null };
}
render() {
if (this.state.model != null) {
return (
<div className="container">
<div className="row">
<div className="col-md">
<h1>{this.state.model.title}</h1>
</div>
</div>
</div>
);
} else {
return (
<div>
<h2>Home</h2>
</div>
);
}
}
componentDidMount() {
const data = this.props.getPage("1");
console.log(data);
this.setState({
model: data,
});
}
}
export default WithBackend(Page);
Here is the HOC component WithBackend: I am not sure if i should be setting the state on this class on in the class that is being wrapped.
When i debug the code in the getPage method, in the setState part i see the data being populated from the backend server.
import React from "react";
import ContentService from "./ContentService";
const WithBackend = (WrappedComponent) => {
class HOC extends React.Component {
constructor() {
super();
this.contentService = new ContentService();
this.getPage = this.getPage.bind(this); // <-- Add this
}
getPage(id) {
this.contentService
.getPage(id)
.then((response) => response.json())
.then((data) => {
this.setState({ model: data });
})
.catch((e) => {
console.log(e);
});
}
render() {
return <WrappedComponent getPage={this.getPage} {...this.props} />;
}
}
return HOC;
};
export default WithBackend;
and here is the contentService which only returns a promise:
class ContentService {
pageUrl = process.env.REACT_APP_BASE_URL + "/pages/";
getPage(id) {
const path = this.pageUrl + id;
const fetchPromise = fetch(path, {
method: "GET",
});
return Promise.resolve(fetchPromise);
}
}
export default ContentService;
Could anyone please advice what i am doing wrong?
Thanks in advance.
getPage is an asynchronous method, that should return a promise:
getPage(id) {
return this.contentService
.getPage(id)
.then((response) => response.json())
.catch((e) => {
console.log(e);
});
}
And then
componentDidMount() {
this.props.getPage("1").then(model => this.setState({ model }));
}

componentDidMount not firing in React

import React from 'react';
import ReactDOM from 'react-dom';
import $ from 'jquery';
import Post from './components/Post.jsx';
import Feed from './components/Feed.jsx';
class App extends React.Component {
constructor() {
super();
this.state = {
view: 'feed',
collection: ''
}
}
componentDidMount() {
console.log('component did mount')
this.getCollection();
}
async getCollection() {
try {
const response = await fetch('/api/page');
const responseJSON = await response.json();
this.setState({ collection: responseJSON }, () => {
console.log("App Component - getCollection() State Updated", this.state);
});
} catch (error) {
console.log("App Component - getCollection() error", error);
}
}
render() {
return (
<div>
Text
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
Can't get component did mount to function. Trying to make an ajax request to my mongodb and render that on the client side. All I need to do now is make a state change setting the collection to the information I get back. I tried using an Ajax request but that didnt work. Now I'm implementing an async fetch call as per the suggestion of another contributor.
the nonworking ajax request:
As of now, componentDidMount is still not being triggered and the collection property of the state is still an empty string.
I would recommend using the Fetch API for AJAX calls and making use of ES6 Async/Await, since importing an entire library just for Ajax seems a bit overkill.
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
collection: ''
}
this.getCollection = this.getCollection.bind(this)
}
componentDidMount() {
console.log('component did mount')
this.getCollection();
}
async getCollection() {
try {
const response = await fetch('/api/blogs');
const responseJSON = await response.json();
this.setState({ collection: responseJSON }, () => {
console.log("App Component - getCollection() State Updated", this.state);
});
} catch (error) {
console.log("App Component - getCollection() error", error);
}
}
render() {
return (
<div>
<h1>Welcome</h1>
</div>
);
}
}
ReactDOM.render(<App /> , document.getElementById('app'));
I'm not sure what you're doing with your render, but I've left it out. Hopefully, this will shed some light on how best to perform what you want.
To get componentDidMount to fire you need the render function. Because first the component renders and then it calls the function componentDidMount.
I think adding this to your class should solve your problem.
render() {
return null;
}

React: Http request response not displaying in render()

Can someone tell me what is wrong with my code below? I am making an HTTP request to Darksky API using 'superagent' and then trying to display the result in an h2 which isn't working. I tried logging it to console and it works perfectly but if I am trying to display it on the page it doesn't work. Could someone help me out pls, I am new to react and not sure what is going wrong.
import React, { Component } from "react";
import "./Body.css";
import Request from "superagent";
class Body extends Component {
constructor() {
super();
this.getData = this.getData.bind(this);
}
getData() {
var url = this.props.apiUrl;
Request.get(url)
.then(response => {
return(JSON.stringify(response.currently.summary));
})
.catch(error => {});
}
render() {
<div>
<h2>
{this.getData()}
</h2>
</div>
}
}
export default Body;
This is the other file where I am importing Body.js :-
import React, { Component } from "react";
import Body from "./Body";
import "./App.css";
class App extends Component {
render() {
return <Body
apiUrl="https://api.darksky.net/forecast/42a9693aecf45c358afbda0022c5cf65/28.5355,77.3910" />;
}
}
export default App;
You need to set your data in the state of the component, it fire new render:
constructor() {
super();
this.getData = this.getData.bind(this);
this.state = {data: {}}
}
componentDidMount() {
var url = this.props.apiUrl;
Request.get(url)
.then(response => this.setState({data: JSON.stringify(response.currently.summary)}))
.catch(error => {});
}
render(){
console.log("your data", this.state.data);
return <div>test</div>;
}
And work with this data with this.state.data.
I advise you to change getData() function to componentDidMount mehtod.
You should use a life cycle method(componentDidMount) with the use of state. It is recommended to make HTTP calls inside the componentDidMount() method.
constructor() {
super();
this.state = {
result: ''
};
}
componentDidMount(){
var url = this.props.apiUrl;
Request.get(url)
.then(response => {
this.setState({
result: JSON.stringify(response.currently.summary)
});
})
.catch(error => {});
}
render() {
<div>
<h2>
{this.state.result}
</h2>
</div>
}

Resources