How read array/object from get Axios in React function [duplicate] - reactjs

I have recently moved from Angular to ReactJs. I am using jQuery for API calls. I have an API which returns a random user list that is to be printed in a list.
I am not sure how to write my API calls. What is best practice for this?
I tried the following but I am not getting any output. I am open to implementing alternative API libraries if necessary.
Below is my code:
import React from 'react';
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
person: []
};
}
UserList(){
return $.getJSON('https://randomuser.me/api/')
.then(function(data) {
return data.results;
});
}
render() {
this.UserList().then(function(res){
this.state = {person: res};
});
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">
{this.state.person.map((item, i) =>{
return(
<h1>{item.name.first}</h1>
<span>{item.cell}, {item.email}</span>
)
})}
<div>
</div>
)
}
}

In this case, you can do ajax call inside componentDidMount, and then update state
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
this.UserList();
}
UserList() {
$.getJSON('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}
render() {
const persons = this.state.person.map((item, i) => (
<div>
<h1>{ item.name.first }</h1>
<span>{ item.cell }, { item.email }</span>
</div>
));
return (
<div id="layout-content" className="layout-content-wrapper">
<div className="panel-list">{ persons }</div>
</div>
);
}
}

You may want to check out the Flux Architecture. I also recommend checking out React-Redux Implementation. Put your api calls in your actions. It is much more cleaner than putting it all in the component.
Actions are sort of helper methods that you can call to change your application state or do api calls.

Use fetch method inside componentDidMount to update state:
componentDidMount(){
fetch('https://randomuser.me/api/')
.then(({ results }) => this.setState({ person: results }));
}

This discussion has been for a while and #Alexander T.'s answer provided a good guide to follow for newer of React like me. And I'm going to share some additional know-how about calling the same API multiple times to refresh the component, I think it's probably a common question for beginners.
componentWillReceiveProps(nextProps), from official documentation :
If you need to update the state in response to prop changes (for
example, to reset it), you may compare this.props and nextProps and
perform state transitions using this.setState() in this method.
We could conclude that here is the place we handle props from the parent component, have API calls, and update the state.
Base on #Alexander T.'s example:
export default class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {person: []};
}
componentDidMount() {
//For our first load.
this.UserList(this.props.group); //maybe something like "groupOne"
}
componentWillReceiveProps(nextProps) {
// Assuming parameter comes from url.
// let group = window.location.toString().split("/")[*indexParameterLocated*];
// this.UserList(group);
// Assuming parameter comes from props that from parent component.
let group = nextProps.group; // Maybe something like "groupTwo"
this.UserList(group);
}
UserList(group) {
$.getJSON('https://randomuser.me/api/' + group)
.then(({ results }) => this.setState({ person: results }));
}
render() {
return (...)
}
}
Update
componentWillReceiveProps() will be deprecated.
Here are only some methods (all of them in Doc) in the life cycle I think that they are related to deploying API in the general cases:
By referring to the diagram above:
Deploy API in componentDidMount()
The proper scenario to have API call here is that the content (from the response of API) of this component will be static, componentDidMount() only fire once while the component is mounting, even new props are passed from the parent component or have actions to lead re-rendering.
The component do check difference to re-render but not re-mount.
Quote from doc:
If you need to load data from a remote endpoint, this is a good place to
instantiate the network request.
Deploy API in static getDerivedStateFromProps(nextProps, prevState)
We should notice that there are two kinds of component updating, setState() in current component would not trigger this method but re-rendering or new props from parent component would.
We could find out this method also fires while mounting.
This is a proper place to deploy API if we want to use the current component as a template, and the new parameters to make API calls are props coming from parent component.
We receive a different response from API and return a new state here to change the content of this component.
For example:
We have a dropdown list for different Cars in the parent component, this component needs to show the details of the selected one.
Deploy API in componentDidUpdate(prevProps, prevState)
Different from static getDerivedStateFromProps(), this method is invoked immediately after every rendering except the initial rendering. We could have API calling and render difference in one component.
Extend the previous example:
The component to show Car's details may contain a list of series of this car, if we want to check the 2013 production one, we may click or select or ... the list item to lead a first setState() to reflect this behavior (such as highlighting the list item) in this component, and in the following componentDidUpdate() we send our request with new parameters (state). After getting the response, we setState() again for rendering the different content of the Car details. To prevent the following componentDidUpdate() from causing the infinity loop, we need to compare the state by utilizing prevState at the beginning of this method to decide if we send the API and render the new content.
This method really could be utilized just like static getDerivedStateFromProps() with props, but need to handle the changes of props by utilizing prevProps. And we need to cooperate with componentDidMount() to handle the initial API call.
Quote from doc:
... This is also a good place to do network requests as long as you
compare the current props to previous props ...

I would like you to have a look at redux
http://redux.js.org/index.html
They have very well defined way of handling async calls ie API calls, and instead of using jQuery for API calls, I would like to recommend using fetch or request npm packages, fetch is currently supported by modern browsers, but a shim is also available for server side.
There is also this another amazing package superagent, which has alot many options when making an API request and its very easy to use.

You can also fetch data with hooks in your function components
full example with api call: https://codesandbox.io/s/jvvkoo8pq3
second example: https://jsfiddle.net/bradcypert/jhrt40yv/6/
const Repos = ({user}) => {
const [repos, setRepos] = React.useState([]);
React.useEffect(() => {
const fetchData = async () => {
const response = await axios.get(`https://api.github.com/users/${user}/repos`);
setRepos(response.data);
}
fetchData();
}, []);
return (
<div>
{repos.map(repo =>
<div key={repo.id}>{repo.name}</div>
)}
</div>
);
}
ReactDOM.render(<Repos user="bradcypert" />, document.querySelector("#app"))

1) You can use Fetch API to fetch data from Endd Points:
Example fetching all Github repose for a user
/* Fetch GitHub Repos */
fetchData = () => {
//show progress bar
this.setState({ isLoading: true });
//fetch repos
fetch(`https://api.github.com/users/hiteshsahu/repos`)
.then(response => response.json())
.then(data => {
if (Array.isArray(data)) {
console.log(JSON.stringify(data));
this.setState({ repos: data ,
isLoading: false});
} else {
this.setState({ repos: [],
isLoading: false
});
}
});
};
2) Other Alternative is Axios
Using axios you can cut out the middle step of passing the results of
the http request to the .json() method. Axios just returns the data
object you would expect.
import axios from "axios";
/* Fetch GitHub Repos */
fetchDataWithAxios = () => {
//show progress bar
this.setState({ isLoading: true });
// fetch repos with axios
axios
.get(`https://api.github.com/users/hiteshsahu/repos`)
.then(result => {
console.log(result);
this.setState({
repos: result.data,
isLoading: false
});
})
.catch(error =>
this.setState({
error,
isLoading: false
})
);
}
Now you can choose to fetch data using any of this strategies in componentDidMount
class App extends React.Component {
state = {
repos: [],
isLoading: false
};
componentDidMount() {
this.fetchData ();
}
Meanwhile you can show progress bar while data is loading
{this.state.isLoading && <LinearProgress />}

Render function should be pure, it's mean that it only uses state and props to render, never try to modify the state in render, this usually causes ugly bugs and decreases performance significantly. It's also a good point if you separate data-fetching and render concerns in your React App. I recommend you read this article which explains this idea very well. https://medium.com/#learnreact/container-components-c0e67432e005#.sfydn87nm

This part from React v16 documentation will answer your question, read on about componentDidMount():
componentDidMount()
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here. If you
need to load data from a remote endpoint, this is a good place to
instantiate the network request. This method is a good place to set up
any subscriptions. If you do that, don’t forget to unsubscribe in
componentWillUnmount().
As you see, componentDidMount is considered the best place and cycle to do the api call, also access the node, means by this time it's safe to do the call, update the view or whatever you could do when document is ready, if you are using jQuery, it should somehow remind you document.ready() function, where you could make sure everything is ready for whatever you want to do in your code...

As an addition/update to Oleksandr T.'s excellent answer:
If you use class components, backend calls should happen in componentDidMount.
If you use hooks instead, you should use the effect hook
For example:
import React, { useState, useEffect } from 'react';
useEffect(() => {
fetchDataFromBackend();
}, []);
// define fetchDataFromBackend() as usual, using Fetch API or similar;
// the result will typically be stored as component state
Further reading:
Using the Effect Hook in the official docs.
How to fetch data with React Hooks? by Robin Wieruch

A clean way is to make an asynchronous API call inside componentDidMount with try/catch function.
When we called an API, we receive a response. Then we apply JSON method on it, to convert the response into a JavaScript object. Then we take from that response object only his child object named "results" (data.results).
In the beginning we defined "userList" in state as an empty array. As soon as we make the API call and receive data from that API, we assign the "results" to userList using setState method.
Inside the render function we tell that userList will be coming from state. Since the userList is an array of objects we map through it, to display a picture, a name and a phone number of each object "user". To retrieve this information we use dot notation (e.g. user.phone).
NOTE: depending on your API, your response may look different. Console.log the whole "response" to see which variables you need from it, and then assign them in setState.
UserList.js
import React, { Component } from "react";
export default class UserList extends Component {
state = {
userList: [], // list is empty in the beginning
error: false
};
componentDidMount() {
this.getUserList(); // function call
}
getUserList = async () => {
try { //try to get data
const response = await fetch("https://randomuser.me/api/");
if (response.ok) { // ckeck if status code is 200
const data = await response.json();
this.setState({ userList: data.results});
} else { this.setState({ error: true }) }
} catch (e) { //code will jump here if there is a network problem
this.setState({ error: true });
}
};
render() {
const { userList, error } = this.state
return (
<div>
{userList.length > 0 && userList.map(user => (
<div key={user}>
<img src={user.picture.medium} alt="user"/>
<div>
<div>{user.name.first}{user.name.last}</div>
<div>{user.phone}</div>
<div>{user.email}</div>
</div>
</div>
))}
{error && <div>Sorry, can not display the data</div>}
</div>
)
}}

As best place and practice for external API calls is React Lifecycle method componentDidMount(), where after the execution of the API call you should update the local state to be triggered new render() method call, then the changes in the updated local state will be applied on the component view.
As other option for initial external data source call in React is pointed the constructor() method of the class. The constructor is the first method executed on initialization of the component object instance. You could see this approach in the documentation examples for Higher-Order Components.
The method componentWillMount() and UNSAFE_componentWillMount() should not be used for external API calls, because they are intended to be deprecated. Here you could see common reasons, why this method will be deprecated.
Anyway you must never use render() method or method directly called from render() as a point for external API call. If you do this your application will be blocked.

You must try "axios" library for API call.
Instead of direct using jQuery.
Thanks.

It would be great to use axios for the api request which supports cancellation, interceptors etc. Along with axios, l use react-redux for state management and redux-saga/redux-thunk for the side effects.

Related

will puting function inside render method slow my app

i am learning react, i am django developer and i am getting data from api and is working fine, just for a fun i wanted to update data without clicking reload, so what i did is put fetch function inside render which is looping , and it is giving real time feel, but i want to know that will doing this like this slow my app like in vannila js, since it is rendering again and again, but since react uses virtual dom i have soft side on this technique
import React from 'react';
class FetchRandomUser extends React.Component {
constructor(){
super();
this.state = {
data: [],
};
}
fetchdata(){
fetch('http://127.0.0.1:8000/api')
.then(response=>response.json())
.then((data)=>{
this.setState({
data:data,
});
console.log(data);
});
}
componentDidMount(){
this.fetchdata();
}
render() {
this.fetchdata();
const dat = this.state.data;
const rows = dat.map((emp)=>
<div>
<h1>{emp.id}</h1>
<h1>{emp.title}</h1>
<h1>{emp.body}</h1>
</div>
);
return (
<div>
{rows}
</div>
)
}
}
export default FetchRandomUser;
You shouldn't use fetch inside render method. Because in your code when this.fetchData() is run, as you can see you are setting the state in that method using setState(), and setState itself calls render method so your code will result in an infinite loop.
Also You shouldn't use fetch inside render method because every time render is called your fetchData() will run which will slow your program as it takes time to fetch data from a server.

How to properly dispatch the same action twice in a single React Component

I am trying to understand the best approach to a data fetching challenge I am facing with redux in my react app.
In short, I need to dispatch the same fetch (in this case, fetchPlayerSeasonStats) twice, and save both fetches of data. The first fetch grabs statistics for a single player (via the fetches optional 1st paramter thisPlayerId), and the 2nd fetch omits the paramter and fetches a much larger dataset.
What I've attempted to do below is the following:
(a) fetch playerSeasonStats the first time
(b) in componentDidUpdate(), check that the first fetch was completed (the if condition checking array legnths).
(c) if condition met, use state variable thisPlayersSeasonStats to store the original fetch of data.
(d) then, refetch the larger dataset with another dispatched action.
... other than the warning I'm receiving saying "do not update state in componentDidMount", in general I'm not sure if this approach is correct or if it is an "anti-pattern" / bad React/Redux coding style. I'd like to make sure I'm doing this right so any review of the code below (in particular the componentDidUpdate() function) would be greatly appreciated!
Thanks!
// Import React Components
import React, { Component } from 'react';
import { connect } from 'react-redux';
// Import Fetches
import { fetchPlayerSeasonStats } from '../../../actions/...';
// Create The Component
class MyComponentHere extends Component {
constructor(props) {
super(props);
this.state = {
thisPlayerSeasonStats: []
};
}
componentDidMount() {
let thisPlayerId = this.props.playerInfo._id;
this.props.dispatch(fetchPlayerSeasonStats(thisPlayerId, this.props.appSeason.value));
}
componentDidUpdate(prevProps) {
console.log('prevProps: ', prevProps);
if (this.props.appSeason !== prevProps.appSeason) { this.refetchTeamsStats(); }
if (prevProps.playerSeasonStats.length === 0 && this.props.playerSeasonStats.length === 1) {
this.setState({ thisPlayerSeasonStats: this.props.playerSeasonStats });
this.props.dispatch(fetchPlayerSeasonStats(null, this.props.appSeason.value));
}
}
render() {
// Handle Initial Loading Of Data
if (this.state.thisPlayerSeasonStats.length === 0) { return <LoadingSpinner />; }
// The Return
return (
<div> Return Dont Matter For here </div>
);
}
}
function mapStateToProps(reduxState) {
return {
playerSeasonStats: reduxState.playerSeasonStatsReducer.sportsData,
loading: (reduxState.playerSeasonStatsReducer.loading),
error1: reduxState.playerSeasonStatsReducer.error
};
}
export default connect(mapStateToProps)(MyComponentHere);
The answer is simple.
Lets look at how redux-thunk works.
Redux Thunk middleware allows you to write action creators that return a function instead of an action
I think this is what fetchPlayerSeasonStats essentially do. It returns some async function that fetch players. Redux-thunk helps dispatch it (I think you use Redux-thunk. In case of you use some other async middleware, it should work essentially the same).
So we can write action creator that will return function (as fetchPlayerSeasonStats) but inside will dispatch not actions but another function. So we'll have function dispatching function which will dispatch action :-)
For example
fetchAllPlayerStats (thisPlayerId, appSeasonValue) => dispatch => {
dispatch(fetchPlayerSeasonStats(thisPlayerId, appSeasonValue));
dispatch(fetchPlayerSeasonStats(null, appSeasonValue));
}
Then you can use this.props.dispatch(fetchAllPlayerStats(thisPlayerId, this.props.appSeason.value)) from componentWillMount to fetch all data at once.
Tip. Current implementation of fetchAllPlayerStats will get all data at once. If you add async/await keywords you'll get firstly data for a single player and then larger data set. Modified version will look like
fetchAllPlayerStats (thisPlayerId, appSeasonValue) => async dispatch => {
await dispatch(fetchPlayerSeasonStats(thisPlayerId, appSeasonValue));
await dispatch(fetchPlayerSeasonStats(null, appSeasonValue));
}
Here is simple example to showcase logic

Set state before render react

I have the App.js, inside of it a component. Name it Example. I want to set the Example's props with the App.states. For an example, Example has the props: name,id. In the App i want to fetch the data i want from the server, and set it as the App's states, then assign them to the Example's props, before the app renders it. The problem here is that i read about componentWillMount(), but the React documentation says that it's not the best way. Then, what's the best way?
General approach is to store ajax response inside App and when response has been saved into the state, render Example component with data fetched from response
class App extends React.Component
state = { response: null }
componentDidMount() {
runYouFetchHere
.then((response) => this.setState({ response }))
}
render() {
const { response } = this.state
if (response === null) { return null }
return (
<Example name={response.name} id={response.id} />
)
}
}
When there is an update in react component then any of the following update methods are called.
https://reactjs.org/docs/react-component.html#updating
Trying to set the state inside
componentWillRecieveProps(){
this.setState({ })
}

How to setState() in React/Apollo with graphQL

I am trying to setState() to a query result I have from graphQL, but I am having difficulty finding out how to do this because it will always be loading, or it's only used from props.
I first set the state
constructor (props) {
super(props);
this.state = { data: [] };
Then I have this query
const AllParams = gql`
query AllParamsQuery {
params {
id,
param,
input
}
}`
And when it comes back I can access it with this.props.AllParamsQuery.params
How and when should I this.setState({ data: this.props.AllParamsQuery.params }) without it returning {data: undefined}?
I haven't found a way to make it wait while it's undefined AKA loading: true then setState. I've tried componentDidMount() and componentWillReceiveProps() including a async function(){...await...} but was unsuccessful, I am likely doing it wrong. Any one know how to do this correctly or have an example?
EDIT + Answer: you should not setstate and just leave it in props. Check out this link: "Why setting props as state in react.js is blasphemy" http://johnnyji.me/react/2015/06/26/why-setting-props-as-state-in-react-is-blasphemy.html
There is more to the problem to update props, but some great examples can be found at this app creation tutorial: https://www.howtographql.com/react-apollo/8-subscriptions/
A simple solution is to separate your Apollo query components and React stateful components. Coming from Redux, it's not unusual to transform incoming props for local component state using mapStateToProps and componentWillReceiveProps.
However, this pattern gets messy with Apollo's <Query />.
So simply create a separate component which fetches data:
...
export class WidgetsContainer extends Component {
render (
<Query query={GET_WIDGETS}>
{({ loading, error, data }) => {
if (loading) return <Loader active inline="centered" />;
const { widgets } = data;
return (
<Widgets widgets={widgets} />
)
}}
</Query>
)
}
And now the Widgets components can now use setState as normal:
...
export class Widgets extends Component {
...
constructor(props) {
super()
const { widgets } = props;
this.state = {
filteredWidgets: widgets
};
}
filterWidget = e => {
// some filtering logic
this.setState({ filteredWidgets });
}
render() {
const { filteredWidgets } = this.state;
return (
<div>
<input type="text" onChange={this.filterWidgets} />
{filteredWidgets.count}
</div>
)
}
}
What is the reason behind setting it to state? Keep in mind, Apollo Client uses an internal redux store to manage queries. If you're trying to trigger a re render based on when something changes in the query, you should be using refetchQueries(). If you absolutely need to store it in local state, I would assume you could probably compare nextProps in componentWillReceiveProps to detect when loading (the value that comes back when you execute a query from apollo client) has changed, then update your state.
I had a similar issue (although it was happening for a totally different reason). My state kept getting set to undefined. I was able to solve it with a React middleware. It made it easy to avoid this issue. I ended up using superagent.
http://www.sohamkamani.com/blog/2016/06/05/redux-apis/

React - constructor() and componentDidMount

I am using Redux to create my pagination. My problem is, in constructor I ran a method that will parse the url and check if there is anything about pagination. Then it runs an action that will put the data in my store. It all runs smoothly.
Then, I have the componentDidMount method when I run another action - fetching data. And there I use those props I have previously pushed. Unfortunately, the store is at its initial state.
My (simplified) code:
class NewsList extends React.Component {
constructor(props) {
super(props);
this.profile = document.getElementById('articles').getAttribute('data-profile');
this.parseHash();
}
parseHash() {
/* Method for parsing navigation hash
---------------------------------------- */
const hash = window.location.hash.replace('#', '').split('&');
const data = {
page: 1,
offset: 0
};
// hash parsing
this.props.setPagination(data);
}
componentDidMount() {
this.loadNews();
// If I use
// setTimeout(() => { this.loadNews(); })
// it works, but I feel this is hack-ish
}
loadNews() {
this.props.update({
current: {page: this.props.NewsListReducer.current.page, offset: this.props.NewsListReducer.current.offset},
next: {page: this.props.NewsListReducer.next.page, offset: this.props.NewsListReducer.next.offset}
});
}
}
When I console.log(this.props.NewsListReducer), I am getting undefined for both current and next object, but when I use Redux DevTools, the data is there. What can I do?
It seems like there's some asynchronicity somewhere in there. You're probably using react-redux right? I think the asynchronicity comes from the connected component, as it uses setState when the store state has changed. setState schedules an asychronous state update.
Therefore this.props.NewsListReducer isn't up to date in componentDidMount().
I guess this.props.update is an action that will fetch the news, right? Why is it necessary that you provide the paging information from the component to it? E.g. with redux-thunk you can access the store state before dispatching an action. This could be your chance for reading the (up to date) paging information.
E.g.
export function fetchNews() {
return (dispatch, getState) => {
getState().NewsListReducer.current.page; // Up to date
...
fetch(...).then(() =>
dispatch({...}));
}
}
Btw. it could be a good idea to not call your state *Reducer. It's the reducer managing the state, but the reducer isn't part of the state in that manner.

Resources