Where to make API hits ComonentDidMount or in ComonentDidUpdate [React.JS] - javascript

In an interview, an interviewer asked me that where should you make API-hits in a simple react application? Meaning in which life-cycle method in a Class-Component. I knew the answer to be ComponentDidMount - because it is the first life-cycle method where we get complete rendered dom meaning dom is now ready!
Then he asked, but why NOT in comonentDidUpdate?
Now, I told him what I had read somewhere, I don't know the exact answer of this -- except ComponentDidMount runs first, so make it there.
Now, can someone tell me if my answer was correct? Or should we make API-hits in ComponentDidUpdate()?
I am confused. Kindly, someone explain with reasoning? Thanks in Advance!

It depends on when you want to call the API:
If an API call is done only once then do componentDidMount
If after render based on some state, you want to fetch data again then do it in componentDidUpdate
EDIT:
Same scenarios can be handled within functional components using useEffect hook as follows:
1- Only runs the first time when the components render same as componentDidMount:
useEffect(() => {
// Run only once when the component renders
}, []); // Pass empty array as dependencies
2- Run every time when component renders either by props change or by local state change same as componentDidUpdate without comparing previous and current props:
useEffect(() => {
// Run every time the component re-renders including the first time
}); // Do NOT pass array dependencies
3- Run only when particular props change, same as componentDidUpdate but with props comparison:
useEffect(() => {
// Run only when the component prop1 and prop2 changes
}, [prop1, prop2]); // Pass props as array dependencies
Reference: Using the Effect Hook

Lets take an example scenario.
You have a profile page and it has a text box which allows you to update tags.
You do a fetch for the whole profile in the componentDidMount to get all the details and show the content.
And then componentDidUpdate will have to be used for something like the update on tags, lets say you do a fetch to get tags based on the user input for every 3 letters the user type. then you use componenDidUpdate to check the state and do the call.
If you think of the same in functional components we'll have to use useEffect.
useEffect(()=>{},[]);
See the array of dependecies, if you pass an empty array it would act similar to componentDidMount.
And the componentDidUpdate
useEffect(()=>{},[tagText]);
Here the effect will run only when a change it done to the tagText, but the componenDidUpdate would be different as you will have to compare the previous state and decide whether the text is updated.

According the Official React Documentation (Link):
componentDidMount
componentDidMount() is invoked immediately after a component is mounted (inserted into the tree). 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().
componentDidUpdate()
componentDidUpdate() is invoked immediately after updating occurs.
This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component
has been updated. This is also a good place to do network requests as
long as you compare the current props to previous props (e.g. a
network request may not be necessary if the props have not changed).
Check out this link for a complete big picture. This will be handy while learning react.

Related

ComponentWillMount can't find an variable

I have already created a react native CLI project. Now every time when user come to my screen I want to update some value. In my home screen, I have three top tabs. When the user clicks on the 3rd tab. I need to update some values every time.
For those three tabs, I am using: ScrollableTabView
import ScrollableTabView, { DefaultTabBar } from 'react-native-scrollable-tab-view';
I used this below code for every time to update the values. But this is calling only for one time:
useEffect(() => {
alert('i called');
})
Then i tried :
import React, { useEffect, Component } from 'react';
componentWillMount( () => {
alert('called ');
})
But I am getting error like cant the variable componentWillMount.
How can I solve? Please help.
Thanks.
It seems like you are using the new React Native way by using hooks and functional components only.
It is not possible to use reacts lifecycle methods anymore - you will have to write your own componentWillMount code. Take a look at the docs or take a look at this answer:
How to use componentWillMount() in React Hooks?
~Faded
So, going back to React basics. There are 2 ways of declaring a component: using a Class based component or using a functional component which you are clearly using.
Before React Hooks, we couldn't have state in our functional components, that means that you cannot use the LifeCycle Methods from a Class based Component. I really suggest you to read the documentation. Instead, we can use hooks to get the same behavior.
Now, the useEffect hook will receive 2 parameters: the first one is the callback we are going to execute, and the second is an array of dependencies that will trigger that hook. It means that the hook will watch for changes in the variables you put in your array. Since you are not declaring an array of dependencies, the hook will be executed on every render.
So, what's exactly the problem? The problem is that declaring your tabs with react-native-scrollable-tab-view won't unmount the components for each tab, there are no variations, that's because your alert log is only being logged once. Because there are no more renders, just once.
Looking at the docs, you might be using onChangeTab method, which apparently will be called when changing the tab.

Can componentDidMount share data between pages?

I am still learning React and I apologize if this is a stupid question. I am currently planning about the architecture of my simple App.
I am using Next.js for this project
I have a component that calls the third party API for data every 5 seconds. The data is shared among all pages in the app.
If the component that is fetching the data is not on the main/home page. Are there anyways for homepage to get the data it needs from another page?
For example [Below are all pages]
index.js // Plain simple page that displays current weather and top music
http://localhost:3000/
weatherforecast.js //Using componentDidMount every 5 seconds to fetch Weather Data
http://localhost:3000/weatherforecast
musicplaylist.js //Using componentDidMount every 5 seconds to fetch Weather
http://localhost:3000/musicplaylist
The data shown in homepage has to be refreshed every 5 seconds if there are changes to the following data in weatherforecast.js and musicplaylist.js
I had this in mind but I have a feeling that it's not the right way to do it.
In the Homepage.js, include WeatherForecast and MusicPlaylist components to fetch the data. If this is the case, it seems like I am repeating the same principle in every page.
I found out about Redux which store states as a global object. But how does the state know when to update. But before we getting to state, I am still not sure if components on another page can fetch the data without the user accessing the page.
Thanks for reading this question.
For a pure React solution, you need to utilize other lifecycle methods available for React. componentDidMount is a good spot for making API requests, so you're in the right place to start. However, componentDidMount occurs only once, right after the componentWillMount and after the DOM is ready with a complete render of the component. Setting your API call to run at a set interval will not trigger a rerender of the component, no any of it's sub components. Instead, use componentWillRecieveProps to add your interval request logic. After each interval completes, run setState with the new data from the request to update the default state defined in your constructor. As a bonus step to improve performace, follow up with function that returns a bool in componentShouldUpdate. This way you can strictly define how and when and what is causing any and all component re-rendering.
Redux is an excellent solution to take care of what you want to do. Personally always use it in my React projects to manage state. Using Redux, you could make your API calls still occur in componentDidMount, however the call can hook into your Redux store and update your initial state. The frees you up from have to worry about the local state of your component and how to go about conditionally rendering everything else in your app. Plus Redux abstracts other functions you need, like re-running your fetchToAPI in certain intervals, into their own source (known as Action Creators). Action Creators hook directly in the Redux store so that when one is used, the resulting state diff is passed to Redux's Reducers, which in turn update the application global state. Then all that needs to be done is to have all of your components that need re-rendering on global state change to listen for state changes that occur in the Redux store and conditionally re-render based on the diff of the store from prevState => newProps. This can be setup fairly easy using the boolean check in componentShouldUpdate.
Hope this helps! Cheers.
Redux passes the data throughout the connected components via prop, given that a component is mounted whenever it receives new props a new render cycle is initiated so it will reflect your changes, moreover if you dont want to use Redux you can try using the new context api which is easy to implement and will also be suitable for your solution here is a tutorial on how to use it
To pass data between pages in next.js you will have to use Redux. The idea of Redux is to have a single source of truth. In redux you update the state by calling actions. To update redux state you have to create something called action creators which dispatches action to update the state.
Answer to your question as to if components on another page can fetch data without the user accessing the page is that they do not need to access the data. The components are mounted only when they are accessed, so it will fetch the data when they are mounted.
Regarding the state change notification, refer to below image, and hopefully it is worth one thousand of words:
Original article here.

Where is best place to load data from redux-store ?

I use componentDidMount() method in my parent React component to fetch some data from API, which are then saved in redux-state.
In my render method, I have another component which should use data from the global redux-state.
I can send data to my child component via props or I can load them using mapStateToProps().
If I try to console.log or assign data to var from props in my child component constructor or in componentDidMount() method, I'm not getting my data as expected (I'm getting initial state).
Console.log in my render method will show me initial state at first rendering, on the second it will show correct data.
I need point where I'm sure that I have correct data.
...where var a = this.props.somethingFromState; will be assigned correctly.
Is render method best place and what is best practice for doing this in general. Thanks.
There are a couple of questions here.
With respect to where to load data, componentDidMount is indeed the correct place to call ajax methods and load data as suggested by the facebook docs.
In terms of whether or not to then pass that data down to child components via props, I like to follow Dan Abramov's (the author of redux) guid on presentational vs container components. In short, try to have a container component that loads your data and does all the logic, and then pass whatever is necessary down to presentational components via props, making them pure functions if possible.
In terms of when you will have the correct data, check the React lifecycle docs above. You will see that the constructor fires, then componentWillMount, then render, then componentDidMount. So if your ajax call gets the data in componentDidMount, you will have called render first, resulting in no data being loaded on the already mounted component. Most people solve this by putting a spinner until the data has finished loading from ajax. A simple if statement should accomplish this

Render after componentWillReceiveProps and before componentDidUpdate

Here's my problem:
I'm new at reactjs and I'm trying to make this app using SWAPI (swapi.co). For now I need to list characters and some info about them. The problem is I have this component called SelectedCharacter that returns some info about a character that was selected in a div.
The moment a character is passed through props to this component, I get a response via xmlhttp and the info is displayed. The thing is that I want to put a "Loading..." message while the data is fetched. This is how I was trying to figure it out:
I set up the componentWillReceiveProps function, where I test if I'll need to load stuff and the componentDidUpdate, where I fetch the data from this api and update the status.
I know, from react life cycle, that a render is called between componentWillReceiveProps and componentDidUpdate, and it indeed is.
I expected, then, that if I did this:
render() {
if (criteria) {
return <div>Loading...</div>
}
}
The thing is: even if this criteria is true (I tested it using console.log()), the message doesn't show until the next re-render. Am I doing anything too wrong here? If it helps, my code is at github.com/piubellofelipe/StarWars, the problem is at the selected_characters.js, in the src paste.
Thanks
I've been looking at your code, trying to work this out for you and I don't have any concrete answers for you, but I've noticed a few things that may be making things a bit unpredictable.
1. Calling forceUpdate
componentWillReceiveProps(){
this.setState({loading:true})
this.forceUpdate();
}
Calling setState will trigger a render, so the call the forceUpdate is not required here. This means there are more renders occurring than you might expect.
I believe this may be the cause of your issue for a pretty complicated reason. From the setState docs
... setState() is also asynchronous, and multiple calls during the same cycle may be batched together.
And from the forceUpdate docs
Calling forceUpdate() will cause render() to be called on the component...
My theory is that the call render triggered by setState, asynchronously setting loading to true, is being delayed and the one from forceUpdate is sneaking in first, while loading is still false.
2. Updating props
this.props.selected.moviesList = moviesListNames;
Components should never, ever, update their own props. Usually, this would be stored in state instead.
For more details on this, read this section of the docs and this answer.
3. Importing axios, but not using it
import axios from 'axios'
This one isn't really an issue (other than an unused import), and might just be preparation for where you're heading. axios gives a much nicer developer experience than XMLHttpRequest (in my opinion) for http requests and will clean up the fetchData function a lot, which will make troubleshooting easier.
I hope this helps a bit, and you're enjoying React. Feel free to follow up on any of these points in the comments or as new questions.

ReactJs: How to pass the initial state while rendering a component?

I know I can pass props while rendering a component. I'm also aware of the getInitialState method. But the problem is, getInitialState isn't quite helping because my component doesn't know it's initial state. I do. So I want to pass it while I'm rendering it.
Something like this (pseudo-code):
React.render(<Component initialState={...} />);
I know I could use a prop to work as the initial state but this smells like an anti-pattern.
What should I do?
EDIT FOR CLARITY
Imagine I have a CommentList component. By the time I first render it, the initial state corresponds to the snapshot of current comments from my database. As the user includes comments, this list will change, and that's why it should be a state and not props. Now, in order to render the initial snapshot of comments I should pass it to the CommentsList component, because it has no way to know it. My confusion is that the only way I see to pass this information is through a props which seems to be an anti-pattern.
Disclaimer: Newer versions of React handle this on a different way.
Only permanent components might be able to use props in the getInitialState. Props in getInitialState is an anti-pattern if synchronization is your goal. getInitialState is only called when the component is first created so it may raise some bugs because the source of truth is not unique. Check this answer.
Quoting documentation:
Using props, passed down from parent, to generate state in
getInitialState often leads to duplication of "source of truth", i.e.
where the real data is. Whenever possible, compute values on-the-fly
to ensure that they don't get out of sync later on and cause
maintenance trouble
You can still do:
getInitialState: function() {
return {foo: this.props.foo}
}
As they will be the default props for your app. But as long as you are using a prop to set a value that presumably won't change, you can use the same prop inside of the render function.
<span>{this.props.foo}</span>
This props won't be modified, so no problem using it each time the render is called.
Edited answer:
In this case your initial state should not be a prop, should be an ajax call which populates the comment list.
To quote the React docs:
Using props, passed down from parent, to generate state in getInitialState often leads to duplication of "source of truth", i.e. where the real data is. Whenever possible, compute values on-the-fly to ensure that they don't get out of sync later on and cause maintenance trouble
And:
However, it's not an anti-pattern if you make it clear that synchronization's not the goal here
So if your props include a value and an initialValue, then it's clear that the latter is for initialization, and there's no confusion.
See the React docs for code examples.
If you know the state then I would tend to argue that the component you are rendering is not really in control of it. The idea in React is that any particular piece of state lives in only a single location.
After seeing the other answers, and studying a little bit about it, I've come to this conclusion:
If you are rendering React in the client (compiled or not), which is the default approach, you should try to make an extra Ajax call from inside your component to get the initial state. That is, don't use props. It's cleaner and less error prone.
However, if you are rendering in the server (Node.js or ReactJs.NET), there's no reason to make this extra Ajax call for each request.. Besides, it's not SEO friendly. You want the complete page to come as the result of your request (including data). So, as #RandyMorris pointed out, in this case it's ok to use props as the initial state, as long as it's exclusively the initial state. That is, no synchronization.

Categories

Resources