React api only fetches if data is not being used - javascript

I have two state items inside redux store, both of which fetches data from the API.
componentDidMount() {
this.props.postMDBConfig(`https://api.themoviedb.org/3/configuration?api_key=${this.props.apiKey}`);
this.props.postMoviePopular(`https://api.themoviedb.org/3/movie/popular?api_key=${this.props.apiKey}&language=en-US&page=1&region=US`)
}
But as soon as I pass (or even use in current component) the information down to a child and use the data, it does not fetch.
render() {
return (
<ItemCarousel MDBConfig={this.props.config} items={this.props.moviesPopular}/>
);
}
class ItemCarousel extends React.Component {
render() {
const slider = (
<AwesomeSlider cssModule={AwesomeSliderStyles}>
<div data-src={`${this.props.config.images.secure_base_url}original${this.props.items[0].poster_path}`}> </div>
</AwesomeSlider>
);
return (
<div>{slider}</div>
);
}
}
Here's what the API fetch looks like when I use vs. don't use the data:
https://ibb.co/G00jzHW (with data not using them)
https://ibb.co/xzkF89R (no data when using the state variables)
I suspect it has something to do with the rendering lifecycle order, but I have already tried componentWillMount and it still does not work compared to componentDidMount.

I think you can have one state variable such as loading initially true. Once your api calls get resolved then you can update the state variable as false.
Now meanwhile you can add this loading check on your component. It will help you out to render the component using the API data.
render() {
const itemCarousel = (this.state.loading) ? '' : <ItemCarousel MDBConfig=
{this.props.config} items={this.props.moviesPopular}/>
return (
{itemCarousel}
)
}
Hope this helps.

Related

How can componentDidMount wait for a value from redux store before executing (without using setTimeout)?

I'm trying to get the authentication info into React component and fetch data related to the authenticated user, but componentDidMount is not getting the auth value from the redux store unless setTimeOut is used.
How Do I go about it?
I tried componentWillReceiveProps() , but that does not work either .
class Dashboard extends Component {
componentDidMount = () => {
console.log(this.props.auth);
setTimeout(
() => console.log(this.props.auth),
100
)
}
Only console.log within setTimeout returns the value
I had this same issue a while back and what helped me was to think about my component as a function.
What I would do is display a spinner, until auth is not null, then render a different view once it is ready.
class Dashboard extends Component {
render = () => {
if(this.props.auth === null){
return <SpinnerComponent />
}
return ....
}
}
What will happen is that:
Before props.auth is ready, the component will render a spinner
After props.auth is ready, the component will render your normal app
Rerendering happens when props are change (ie redux is changed)

How and where to pass JSON data as a prop when creating components

I am getting some Club information from a JSON I want to use in my React component 'Club'. I created a component ClubList in which all Club components with their corresponding name should be created but I don't know where I should make the HTTP request and where to save it, so I can use it in the return statement.
I tried saving all titles in an array but I stopped at the point I had to pass the titles to each Club element. I just started working with ReactJS so I am a basically complete beginner in ReactJS (Not in JS though).
This is the ClubList class
class ClubList extends React.Component {
render() {
return (
<div className="Clublist">
<Club title="Club1" />
<Club title="Club2" />
...
...
...
</div>
)
}
}
And that is the Club class
class Club extends React.Component {
clubProp = {...}
render() {
return (
<div className="Club">
<div className="image-container">
<img src={this.clubProp.imageSrc} width="300px" height="300px"/>
</div>
<h2>{this.clubProp.name}</h2>
<div className="Business-information">
<div className="Business-address">
<p>{this.clubProp.address}</p>
<p>{this.clubProp.city}</p>
<p>{this.clubProp.zipCode}</p>
</div>
<div className="Business-reviews">
<h3>{this.clubProp.category}</h3>
<h3 className="rating">{this.clubProp.rating}</h3>
<p>90 reviews</p>
</div>
</div>
</div>
)
}
}
I will use an API to get the Club-names but I don't know how I can organize the variables to be accessible in the right places since I don't quite grasp how the scopes in React work. I already have the code for getting the JSON ready, just need to know where to put it and how to pass the values in
Your basic syntax would look like the following. Your component will maintain clubs using component state. In the componentDidMount lifecycle function, you can make your api call and then store the results in your component's state. Any time you call setState, your component will re-render.
class ClubList extends React.Component {
state = {
clubs: []
};
componentDidMount = async () => {
const clubs = await this.fetchClubs();
this.setState({ clubs });
}
render() {
const { clubs } = this.state;
return (
<div className="Clublist">
{clubs.map(club => (
<Club title={club.title} />
)}
</div>
);
}
}
Eventually you can pull out state management from all of your components and use something like redux or MobX and let your components focus solely on rendering html.
Where you should make the API request?
Ideally, we use redux-sagas or redux-thunk as middleware while making API requests. However, since, you're just getting started, you could make the API call in the componentDidMount lifecycle method of your ClubList component.
Now, I am assuming that you receive an array of Clubs. You could map over this array and render each Club component.
Where you should store this data?
Common practice is to use a state-management library like redux with react. It helps scale and maintain your app better. However, you could also use the state of the ClubList component to store the data of your API call.
I hope this was helpful.
From the docs, you make API calls in the componentDidMount life-cycle method. I'd recommend looking at the docs for examples:
https://reactjs.org/docs/faq-ajax.html
The docs use the browser's fetch method to make the request, but I'd personally recommend using axios. https://www.npmjs.com/package/axios, since it's a bit more straight forward.
React uses state, which can be passed down through the component tree. As you are using class components, a typical way of getting your JSON data into the app's state would be to fetch the data in the componentDidMount() lifecyle method of your top level component, and run this.setState({clubProp: result.data}) in the fetch/axios callback. You can pass it to children where they are available as props.
I would argue that Redux is overkill - and that it would be better to defer learning it until you have a state management problem. The new hooks implementation and context API will also change best practices for state management. The guy who created Redux says "Flux libraries are like glasses: you’ll know when you need them."
// Here is the simplified example:
class ClubList extends React.Component {
constructor(props) {
super(props);
this.state = {
ClubDetails: [],
};
}
componentDidMount() {
fetch("api link put here in quotes")
.then(results => {
return results.json();
}).then(data => {
let ClubDetails = data;
this.setState({ ClubDetails: ClubDetails });
})
}
render() {
const ClubDetails = this.state.ClubDetails;
const listItems = ClubDetails.map((clubProp,index) =>
<Club key={index} clubProp={clubProp}/>
);
return (
{listItems}
);
}
}
class Club extends React.Component {
render() {
return (
<div className="Club">
<div className="image-container">
<img src={this.clubProp.imageSrc} width="300px" height="300px"/>
</div>........
My recommendation is using fetch middleware in redux to control all datas in props(You can find in redux github examples), besides the responsive data, you can also monitor fetching status across components.

Why aren't my props available to use in my component when I need them?

I keep running into similar issues like this, so I must not fully understand the lifecycle of React. I've read a lot about it, but still, can't quite figure out the workflow in my own examples.
I am trying to use props in a child component, but when I reference them using this.props.item, I get an issue that the props are undefined. However, if the app loads and then I use the React Browser tools, I can see that my component did in fact get the props.
I've tried using componentDidMount and shouldComponentUpdate in order to receive the props, but I still can't seem to use props. It always just says undefined.
Is there something I'm missing in React that will allow me to better use props/state in child components? Here's my code to better illustrate the issue:
class Dashboard extends Component {
state = { reviews: [] }
componentDidMount () {
let url = 'example.com'
axios.get(url)
.then(res => {
this.setState({reviews: res.data })
})
}
render() {
return(
<div>
<TopReviews reviews={this.state.reviews} />
</div>
);
}
}
export default Dashboard;
And then my TopReviews component:
class TopReviews extends Component {
state = { sortedReviews: []}
componentDidMount = () => {
if (this.props.reviews.length > 0) {
this.sortArr(this.props.reviews)
} else {
return <Loader />
}
}
sortArr = (reviews) => {
let sortedReviews = reviews.sort(function(a,b){return b-a});
this.setState({sortedReviews})
}
render() {
return (
<div>
{console.log(this.state.sortedReviews)}
</div>
);
}
}
export default TopReviews;
I'm wanting my console.log to output the sortedReviews state, but it can never actually setState because props are undefined at that point in my code. However, props are there after everything loads.
Obviously I'm new to React, so any guidance is appreciated. Thanks!
React renders your component multiple times. So you probably see an error when it is rendered first and the props aren't filled yet. Then it re-renders once they are there.
The easy fix for this would be to conditionally render the content, like
<div>
{ this.props.something ? { this.props.something} : null }
</div>
I would also try and avoid tapping into the react lifecycle callbacks. You can always sort before render, like <div>{this.props.something ? sort(this.props.something) : null}</div>
componentDidMount is also very early, try componentDidUpdate. But even there, make your that your props are present.
For reference: see react's component documentation

what is right way to do API call in react js?

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.

React JS component renders multiple times in Meteor

I using Meteor 1.3 for this application together with react js and Tracker React.
I have a page to view all available users in the application. This page require user to login to view the data. If user not logged in, it will shows the login form and once logged-in the component will render the user's data.
Main component for the logic.
export default class MainLayout extends TrackerReact(React.Component) {
isLogin() {
return Meteor.userId() ? true : false
}
render() {
if(!this.isLogin()){
return (<Login />)
}else{
return (
<div className="container">
<AllUserdata />
</div>
)
}
}
}
And in the AllUserdata component:
export default class Users extends TrackerReact(React.Component) {
constructor() {
super();
this.state ={
subscription: {
Allusers : Meteor.subscribe("AllUsers")
}
}
}
componentWillUnmount(){
this.state.subscription.Allusers.stop();
}
allusers() {
return Meteor.users.find().fetch();
}
render() {
console.log('User objects ' + this.allusers());
return (
<div className="row">
{
this.allusers().map( (user, index)=> {
return <UserSinlge key={user._id} user={user} index={index + 1}/>
})
}
</div>
)
}
};
The problem is when logged in, it only shows the current user's data. All other user objects are not rendered. If I check on the console, console.log('User objects ' + this.allusers()); show objects being rendered 3 times: the first render only shows the current user's data, the second one renders data for all users (the desired result), and the third one again renders only the current user's data.
If I refresh the page, the user data will be rendered properly.
Any idea why?
React calls the render() method of components many times when it's running. If you're experiencing unexpected calls, it's usually the case that something is triggering changes to your component and initiating a re-render. It seems like something might be overwriting the call to Meteor.users.find().fetch(), which is probably happening because you're calling that function on each render. Try inspecting the value outside of the render method or, better yet, rely on tests to ensure that your component is doing what it should be :)
From https://facebook.github.io/react/docs/component-specs.html#render
The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it's invoked, and it does not read from or write to the DOM or otherwise interact with the browser (e.g., by using setTimeout). If you need to interact with the browser, perform your work in componentDidMount() or the other lifecycle methods instead. Keeping render() pure makes server rendering more practical and makes components easier to think about.
See also:
https://facebook.github.io/react/docs/advanced-performance.html
https://facebook.github.io/react/docs/top-level-api.html#reactdom
https://ifelse.io/2016/04/04/testing-react-components-with-enzyme-and-mocha/

Categories

Resources