I want to fetch data from server & show it inside tables. When I directly put code inside the render it works. But, When I encapsulate inside the addElementsToDisplay function & call that function inside render method it doesn't work. Actually, the function gets called, but response is not rendered in table format. Below is my code:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { Button } from 'semantic-ui-react';
import ResponseRenderer from './responseRenderer';
import "./App.css";
const responseDataContext = React.createContext({});
class App extends Component {
constructor(props) {
super(props);
this.getPastJobs = this.getPastJobs.bind(this);
this.addElementsToDisplay = this.addElementsToDisplay.bind(this);
this.state = { pastJobs: [] }
}
render() {
return (
<div className="App">
<Button onClick={this.getPastJobs}>Get Past Jobs</Button>
<h1> Hello, World! </h1>
{this.addElementsToDisplay()}
</div>
);
}
addElementsToDisplay() {
console.log("state: ", JSON.stringify(this.state));
this.state.pastJobs.map((value, index) => {
return <ResponseRenderer key={Math.random()} data={value} />
});
}
getPastJobs() {
fetch('http://localhost:9090/getPastJobs', {
method: 'POST',
body: JSON.stringify({})
})
.then((response) => {
if (response.status !== 200) {
return;
}
response.json().then((jobs) => {
console.log(jobs);
this.setState({ pastJobs: jobs.data })
});
})
.catch((err) => {
console.log(err.message, err.stack);
});
}
}
export default App;
You are not returning the response and hence it is not rendered, just return the mapped response and it will work fine
addElementsToDisplay() {
console.log("state: ", JSON.stringify(this.state));
return this.state.pastJobs.map((value, index) => {
return <ResponseRenderer key={Math.random()} data={value} />
});
}
Related
The problem occurs when I do try to fire onClick function linked with the ButtonWithDate component ( it is being inherited from the parent component ):
<Button
variant="extendedFab"
onClick={this.props.updateDateAndHour}
color="primary">
Display Date
</Button>
Once I use it, the following error occurs:
TypeError: _services_API__WEBPACK_IMPORTED_MODULE_7__.default.getResponse is not a function
Bind of updateDateAndHour func:
<ButtonWithDate updateDateAndHour={this.updateDateAndHour}></ButtonWithDate></center>
App.js
import React, { Component } from 'react';
import API from './services/api';
import ButtonWithDate from './components/ButtonWithDate';
class App extends Component {
constructor() {
super();
this.state= {
'day': '',
'month': '',
'year': ''
};
this.API = API;
}
updateDateAndHour = () => {
console.log(this);
var self = this;
API.getResponse().then((res) => {
var local_date = res.date.split('-');
self.setState({
day: local_date[0]
})
self.setState({
day: local_date[1]
})
self.setState({
day: local_date[2]
})
});
}
render() {
return (
<div>
<center>
<ButtonWithDate updateDateAndHour={this.updateDateAndHour}></ButtonWithDate></center>
</div>
);
}
}
export default App;
services/api.js
import axios from 'axios';
const URL = 'https://...';
export default class API{
getResponse() {
axios.get(URL)
.then(result => {
return result
})
.catch(error => {
return null;
});
}
};
components/ButtonWithDate.js
import React from 'react';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
export default class ButtonWithDate extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="button-container">
<Button
variant="extendedFab"
onClick={this.props.updateDateAndHour}
color="primary">
Display Date
</Button>
</div>
)
}
}
ButtonWithDate.propTypes = {
onClickButton: PropTypes.func
}
When I do use a console.log along with the function name, I can easily access the content of it:
updateDateAndHour = () => {
console.log(getResponse);
getResponse()...
but once I invoke getResponse(), an exception takes a place.
Screenshot of the error:
first of all sounds like there is a problem with this binding;
class App extends Component {
constructor() {
...
this.updateDateAndHour = this.updateDateAndHour.bind(this);
}
updateDateAndHour(){
...
})
...
}
you can use babel proposal class properties to automatically to those bindings for you instead of manually doing every time
checkout this link for further reading;
However the main problem is in your ./api/ it's simpler to export getResponse() like below
services/api.js
import axios from 'axios';
const URL = 'https://...';
const getResponse = () => {
axios.get(URL)
.then(result => {
return result
})
.catch(error => {
return null;
});
}
export default getResponse;
EDIT:
for the sake the debugging cut getResponse() body in your /api/ and use it in updateDateAndHour(){ ... }) to see if it's working or not ( to see if the problem is in importing or the function itself )
Ok, the issue was connected with with usage of
.then((res) => {
var local_date = res.date.split('-');
self.setState({
day: local_date[0]
})
getResponse() function was not returning a new Promis object. That was the problem.
Quick-fix:
api.js
import axios from 'axios';
const DATE_JSON_URL = 'https://...';
function getResponse() {
return new Promise((resolve, reject) => {
axios.get(URL)
.then(result => {
resolve(result);
})
.catch(error => {
reject(error);
})
})
}
export default getResponse;
I am trying to migrate my previously working local state to redux. Now loading available Players works just fine, but deleting will somehow stop in the playerActions.js file, where I dispatch and then return an API Call. So to further give details here are my code parts in relevance:
PlayerPage.js (Component):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadPlayers, deletePlayer } from '../../redux/actions/playerActions';
import PlayerForm from './playerform';
import PlayCard from './playercard';
import PropTypes from 'prop-types';
import { toast } from 'react-toastify';
class PlayerPage extends Component {
constructor(props) {
super(props);
this.handleDeletePlayer = this.handleDeletePlayer.bind(this);
state = {};
componentDidMount() {
const players = this.props;
players.loadPlayers().catch(err => {
alert('Loading players failed. ' + err);
});
}
handleDeletePlayer = player => {
toast.success('Player deleted');
try {
deletePlayer(player);
} catch (err) {
toast.error('Delete failed. ' + err.message, { autoClose: false });
}
};
render() {
const styles = {
margin: '20px'
};
return (
<div className="container-fluid">
<div>
<h2 style={styles}>Add Player</h2>
<div className="container-fluid">
<PlayerForm handleAddNewPlayer={this.handleAddPlayer} />
</div>
</div>
<hr></hr>
<div>
<h2 style={styles}>Available Player</h2>
<div className="container-fluid">
{this.props.players.map(player => (
<PlayCard
player={player}
key={player.id}
imageSource={`${process.env.API_URL}/${player.profileImg}`}
onDeletePlayer={this.handleDeletePlayer}
/>
))}
</div>
</div>
</div>
);
}
}
PlayerPage.propTypes = {
players: PropTypes.array.isRequired
};
function mapStateToProps(state) {
return {
players: state.players
};
}
const mapDispatchToProps = {
loadPlayers,
deletePlayer
};
export default connect(mapStateToProps, mapDispatchToProps)(PlayerPage);
And the Action being called is in here:
playerActions.js:
import * as types from './actionTypes';
import * as playerApi from '../../api/playerApi';
export function loadPlayersSuccess(players) {
return { type: types.LOAD_PLAYERS_SUCCESS, players };
}
export function deletePlayerOptimistic(player) {
return { type: types.DELETE_PLAYER_OPTIMISTIC, player };
}
export function loadPlayers() {
return function(dispatch) {
return playerApi
.getAllPlayers()
.then(players => {
dispatch(loadPlayersSuccess(players));
})
.catch(err => {
throw err;
});
};
}
export function deletePlayer(player) {
console.log('Hitting deletePlayer function in playerActions');
return function(dispatch) {
dispatch(deletePlayerOptimistic(player));
return playerApi.deletePlayer(player);
};
}
The console.log is the last thing the app is hitting. But the API Call is never made though.
API Call would be:
playerApi.js:
import { handleResponse, handleError } from './apiUtils';
const axios = require('axios');
export function getAllPlayers() {
return (
axios
.get(`${process.env.API_URL}/player`)
.then(handleResponse)
.catch(handleError)
);
}
export function deletePlayer(id) {
return (
axios
.delete(`${process.env.API_URL}/player/${id}`)
.then(handleResponse)
.catch(handleError)
);
}
I was like spraying out console.log in different places and files and the last one I am hitting is the one in playerActions.js. But after hitting it the part with return function(dispatch) {} will not be executed.
So if someone could point me in a general direction I'd be more than grateful.
It looks like you are calling your action creator deletePlayer but you aren't dispatching it correctly. This is why the console.log is being called but not the method that does the request.
I'd recommend taking a look at the documentation for mapDispatchToProps to fully understand how this works. In your example, you should just need to change the call to deletePlayer in your PlayerPage component to this.props.deletePlayer() to use the action creator after it's been bound to dispatch properly.
this how the mapDispatchToProps should be:
const mapDispatchToProps = dispatch => {
return {
load: () => dispatch(loadPlayers()),
delete: () => dispatch(deletePlayer()),
}
}
then call load players with this.props.load() and delete player with this.props.delete()
I can't get my api data from https://randomuser.me/api/
But when I'm using another api like http://dummy.restapiexample.com/api/v1/employees it works.
The error:
import React from "react";
import "./App.css";
import Start from "./start";
function App() {
return (
<div className="App">
<Start />
</div>
);
}
export default App;
start.js
import React, { Component } from "react";
import Axios from "axios";
class Start extends Component {
constructor(props) {
super(props);
this.state = {
results: []
};
}
componentDidMount() {
Axios.get("https://randomuser.me/api/").then(res => {
const results = res.data;
this.setState({ results });
console.log(results);
});
}
render() {
return (
<div>
{this.state.results.map(result => {
return <div>{result.id}</div>;
})}
</div>
);
}
}
export default Start;
Problem is that http://dummy.restapiexample.com/api/v1/employees returns array while https://randomuser.me/api/ returns object. Try changing to
componentDidMount() {
Axios.get("https://randomuser.me/api/").then(res => {
const results = res.data.results;
this.setState({ results });
console.log(results);
});
}
You have to use res.data.results. It comes in results object.
Please check your JSON data
last line you missed the "}]" typo error in http://dummy.restapiexample.com/api/v1/employees
componentDidMount() {
Axios.get("http://dummy.restapiexample.com/api/v1/employees").then(res => {
const results = res.data;
this.setState({ results: results });
});
}
I've got a function in my React app that is calling in componentWillMount lifecycle method.
It grabs data from JSON file and push it to the component's state (it is a text data, I later insert that text into the page).
I'm going to use the same function on many other components, can I separate this function into a separate component to make it reusable?
Here is my code:
import React from 'react';
import axios from 'axios';
import logo from '../img/company_logo.png';
import '../css/header.scss';
import getTextData from './getTextData';
const NumberList = (props) => {
console.log(props.value);
const itemList = props.value;
const listItems = itemList.map(number => (
<li key={number.toString()}>
{number}
</li>
));
return (
<ul>{listItems}</ul>
);
};
export default class Header extends React.Component {
constructor() {
super();
this.state = {};
}
componentWillMount() {
axios.get('./data.json')
.then((res) => {
this.setState({
siteData: res.data,
});
})
.catch((err) => {
console.log(err);
});
}
render() {
// console.log(this.state);
const { siteData } = this.state;
if (siteData) {
console.log(siteData.data.mainPage.navBar);
} else {
return null;
}
return (
<div className="headerWrapper">
<img src={logo} alt="company_logo" id="companyLogo" />
<NumberList value={siteData.data.mainPage.navBar} />
</div>
);
}
}
Yes, create a function and return the repsonse-data, must use async await
//fetchService.js
import axios from 'axios';
export default async function fetchService(){
let responseData = [];
await axios.get('./data.json')
.then((res) => {
responseData = res.data;
})
.catch((err) => {
console.log(err);
});
return responseData;
}
// App.js
import fetchService from './fetchService';
async componentDidMount() {
let tempData = await fetchService();
this.setState({
siteData: tempData,
});
}
i hope this helps!
After get the comments array from post component and pass it to comments component
the logs start to show the error in the screenshot below
the components are:
import React, { Component } from "react";
import axios from "axios";
import Comments from "../components/comments";
class Article extends Component {
constructor(props) {
super(props);
this.state = {
title: "",
error: "",
comment: ""
};
}
componentDidMount() {
this.getComments();
}
getComments = () => {
const {
match: { params }
} = this.props;
return axios
.get(`/articles/${params.id}/comments`, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
}
})
.then(response => {
return response.json();
})
.then(response => this.setState({ comments: response.comments }))
.catch(error =>
this.setState({
error
})
);
};
render() {
return (
<div>
{this.state.title}
<div>
<h2>Comments</h2>
<Comments
getComments={this.getComments}
/>
</div>
</div>
);
}
}
export default Article;
and Comments component
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;
so, I've tried to pass it by props, and set the state on comments component.
and instead of use just comments.map I've tried to use this.state but show the same error in the logs.
So, someone please would like to clarify this kind of issue?
seems pretty usual issue when working with react.
If an error occurs you do:
.catch(error => this.setState({ error }) );
which makes the chained promise resolve to undefined and that is used as comments in the Comments state. So you have to return an array from the catch:
.catch(error => {
this.setState({ error });
return [];
});
Additionally it woupd make sense to not render the Comments child at all if the parents state contains an error.
The other way is checking whether it’s an array and if so check it’s length and then do .map. You have initialized comments to empty array so we don’t need to check whether it’s an array but to be on safer side if api response receives an object then it will set object to comments so in that case comments.length won’t work so it’s good to check whether it’s an array or not.
Below change would work
<div>
{Array.isArray(comments) && comments.length>0 && comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
The first time the comments component renders there was no response yet so comments were undefined.
import React, { Component } from "react";
import PropTypes from "prop-types";
import Comment from "./comment";
import axios from "axios";
import Article from "../screens/article";
class Comments extends Component {
constructor(props) {
super(props);
this.state = {
comments: [],
comment: "",
error: ""
};
this.load = this.load.bind(this);
this.comment = this.comment.bind(this);
}
componentDidMount() {
this.load();
}
load() {
return this.props.getComments().then(comments => {
this.setState({ comments });
return comments;
});
}
comment() {
return this.props.submitComment().then(comment => {
this.setState({ comment }).then(this.load);
});
}
render() {
const { comments } = this.state;
if (!comments) return <p>No comments Available</p>;
return (
<div>
{comments.map(comment => (
<Comment key={comment.id} commment={comment} />
))}
</div>
);
}
}
export default Comments;