I looked around on here and I saw a few helpful answers, but nothing that has helped my problem. my relevant code:
Essentially my idea is to have the onSuccess be a callback function, so that I can finish updating state before i call the child component GameBoard with these props... any idea how to fix this without messing with the async attribute?
Inside of render() you'll want to return an intermediate loading message (or spinner component) that conditionally renders based on a flag. Have your ajax request toggle that flag when finished.
A very basic example...
render() {
if (!this.state.didFetch) return <div>loading...</div>;
return <GameBoard />;
}
Once your ajax request updates state, the render() method will automatically be called again, rendering GameBoard with the props. For more information, check out the component lifecycle documentation — specifically, the section about updating... "An update can be caused by changes to props or state"
As a side note, it's generally considered bad practice to use jQuery with React due to React's concept of a virtual DOM. And if you're not using jQuery elsewhere, you might want to consider replacing it completely with axios.
Related
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.
I am reading up on react lifecycle and am getting a little confused. Some recommend using componentWillMount to make ajax calls:
https://hashnode.com/post/why-is-it-a-bad-idea-to-call-setstate-immediately-after-componentdidmount-in-react-cim5vz8kn01flek53aqa22mby
Calling setState in componentDidMount will trigger another render()
call and it can lead to layout thrashing.
and in other places it says not to put ajax calls in the componentWillMount:
https://medium.com/#baphemot/understanding-reactjs-component-life-cycle-823a640b3e8d
...this function might end up being called multiple times before the
initial render is called so might result in triggering multiple
side-effects. Due to this fact it is not recommended to use this
function for any side-effect causing operations.
Which is correct?
The React docs recommend on using componentDidMount for making network Requests
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.
Calling setState() in this method will trigger an extra rendering, but
it is guaranteed to flush during the same tick. This guarantees that
even though the render() will be called twice in this case, the user
won’t see the intermediate state.
As per the case for componentWillMount:
EDIT:
This lifecycle is deprecated since v16.3.0 of react and is no longer encouraged for usage.However its renamed to UNSAFE_componentWillUpdate and is expected to work till at least v17 of react
Before v16.3.0
An asynchronous call to fetch data will not return before the render happens. This means the component will render with empty data at least once.
There is no way to “pause” rendering to wait for data to arrive. You cannot return a promise from componentWillMount or wrangle in a setTimeout somehow. The right way to handle this is to setup the component’s initial state so that it’s valid for rendering.
To Sum it up
In practice, componentDidMount is the best place to put calls to fetch data, for two reasons:
Using DidMount makes it clear that data won’t be loaded until after
the initial render. This reminds you to set up initial state
properly, so you don’t end up with undefined state that causes
errors.
If you ever need to render your app on the server, componentWillMount will actually be
called twice – once on the server, and again on the client – which is
probably not what you want. Putting the data loading code in
componentDidMount will ensure that data is only fetched from the
client.
componentDidMount is the recommended lifecycle method to make Ajax calls as described in their docs
ComponentDidMount is the place.
But if you have time try to look at Redux and make the requests in actions, as your application grow it will help a lot to manage the app state.
;)
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.
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.
This page of React's docs shows that async requested data should be consumed in the componentDidMount event, while getInitialState initialize the state object to a default empty state.
Is there a reason for that? Maybe getInitialState is not expected to do this or has a different purpose? Is it due to some internal logic of the library?
Note to moderators and answerers: this isn't an opinion-based question: if a reason exists, that would be the answer, however, a good, correct answer to my question could be as well "No, there isn't any particular reason, do whatever you feel better"
getInitialState should be a pure function of props (though they often aren't used). In other words, with the same props getInitialState should return the same data.
componentDidMount is allowed to have dynamic behavior, and cause side effects such as dom manipulation and ajax requests (that's the main intention of it).
A common way to handle this is with an early return of either an empty div, loading message, <div>Loading</div>), or loading indicator (e.g. spinkit).
On first render the spinner will be shown, and then eventually state is updated with data and the main part of render can be run.
render: function(){
// while loading
if (!this.state.data){
return <div className="spinner"></div>
}
// loaded, you can use this.state.data here
return <div>...</div>
}
You wouldn't want to do this because your component will be waiting on the async request and it won't be able to mount until it's finished. If you have html with a couple state variables, let react render first instead of making it wait. I know this is an opinion but it's also a separation of concerns thing as well.