Best way to run a function when page refresh - javascript

I am trying to call a function when the page is refreshed. I adding a state if the page is rendered with the data I got from my backend end but I get an warning message "Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state." Even though it works fine (except with the warning message), I dont think this is the best, most efficient way to do it?
If this is the best, most efficient way, how do I fix the waring message?
function Demo() {
constructor(){
this.state = {
username: "unknown",
rendered: false,
}
this.renderUserProfile = this.renderUserProfile.bind(this);
}
update(){
//code to retrieve data from backend node.js *
this.setState({ username: data });
this.setState({ rendered: true });
}
render(){
if (!this.state.rendered) {
this.update();
}
return (<p>demo</p>)
}
}
Thank you for your help!

Do never change state inside render, because every state (or prop) change will call render again. That is what the warning is telling you: you risk having infinite loops.
There is no need of a state param for "rendered", because your component will call render anyway as soon as this.setState({username: data}); executes. If you want something to happen then, add it in update just after the setState line.
Now let's imagine that you still really want it. If you don't want your component to render when the rendered state changes, then just don't use the React Component state, but any standard class attribute:
class MyComponent extends React.Component {
rendered = false
...
render() {
this.rendered = true
....
}
}
Just be aware that this looks super wrong (and useless) since it tries to go around what the React framework is good at.
Finally, from this code there is no way to know how you intend you have new data coming in. If it is an Ajax call, then you will call this.update with that data in the callback of your Ajax call - certainly not in render.

Related

How to reset state on props change in an already mounted component?

I have a <BlogPost> component which could've been a Stateless Function Component, but turned out as a Class Stateful Component because of the following:
The blogPost items that it renders (receiving as props) have images embedded in their html marked content which I parse using the marked library and render as a blog post with images in between its paragraphs, h1, h2, h3, etc.
The fact is that I need to preload those images before rendering the post content to my client. I think it's a UX disaster if you start reading a paragraph and all of a sudden it moves down 400px because the image that was being loaded has been mounted to the DOM during the time you were reading it.
So I prefer to hold on by rendering a <Spinner/> until my images are ready. That's why the <BlogPost> is a class component with the following code:
class BlogPost extends React.Component {
constructor(props) {
super(props);
this.state={
pending: true,
imagesToLoad: 0,
imagesLoaded: 0
};
}
preloadImages(blogPostMedia) {
this.setState({
pending: true,
imagesToLoad: 0,
imagesLoaded: 0
});
... some more code ...
// Get images urls and create <img> elements to force browser download
// Set pending to false, and imagesToLoad will be = imagedLoaded
}
UNSAFE_componentWillReceiveProps(nextProps) {
if (this.props !== nextProps) {
this.preloadImages(nextProps.singleBlogPost.media);
}
}
componentDidMount() {
this.preloadImages(this.props.singleBlogPost.media);
}
render() {
return(
this.state.pending ?
<Spinner/>
: (this.state.imagesLoaded < this.state.imagesToLoad) ?
<Spinner/>
: <BlogPostStyledDiv dangerouslySetInnerHTML={getParsedMarkdown(this.props.singleBlogPost.content)}/>
);
}
}
export default BlogPost;
At first I was calling the preloadImages() only inside the componentDidMount() method. And that works flawlessly for the first post I render with it.
But as soon as I would click on the next post link; since my <BlogPost>component is already mounted, componentDidMount() doesn't get called again and all the subsequent posts I would render by clicking on links (this is a Single Page App) wouldn't benefit from the preloadImages() feature.
So I needed a way to reset the state and preload the images of the new blogPost received as props inside an update cycle, since the <BlogPost> component it's already mounted.
I decided to call the same preloadImages() function from inside the UNSAFE_componentWillReceiveProps() method. Basically it is reseting my state to initial conditions, so a <Spinner/> shows up right away, and the blog post only renders when all the images have been loaded.
It's working as intended, but since the name of the method contains the word "UNSAFE", I'm curious if there's a better way to do it. Even though I think I'm not doing anything "unsafe" inside of it. My component is still respectful to its props and doesn't change them in anyway. It just been reset to its initial behavior.
RECAP: What I need is a way to reset my already mounted component to its initial state and call the preloadImages() method (inside an update cycle) so it will behave as it was freshly mounted. Is there a better way or what I did is just fine? Thanks.
I would stop using componentWillReceiveProps()(resource). If you don't want the jarring effect, one way you can avoid it is to load the information from <BlogPost/>'s parent, and only once the information is loaded, to pass it into <BlogPost/> as a prop.
But anyway, you can use keys to reset a component back to its original state by recreating it from scratch (resource).
componentWillReceiveProps is deprecated, it's supposed to be replaced with either getDerivedStateFromProps or componentDidUpdate, depending on the case.
Since preloadImages is asynchronous side effect, it should be called in both componentDidMount and componentDidUpdate:
componentDidMount() {
this.preloadImages(this.props.singleBlogPost.media);
}
componentDidUpdate() {
this.preloadImages(this.props.singleBlogPost.media);
}

componentDidMount or componentWillMount which one I need to use

I created a a box similar to twitter using react. I was looking at the react documentation found several component life cycles but not sure which one I should use to improve my code performance: componentDidMount or componentWillMount?
When I type something in my text box I see an update in the console printing the text box value. Can anyone help me understand which method to use and when in this case?
https://jsfiddle.net/c9zv7yf5/2/
class TwitterBox extends React.Component {
constructor(props) {
super(props);
this.state = { enteredTextBoxvalue : '' };
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({enteredTextBoxvalue: event.target.value});
if((event.target.value).length > 3) {
this.setState({className : 'wholeContainer'});
//console.log("long characters");
}
}
render() {
return (<div>Hello {this.props.name}
<textarea className={this.state.className}
value={this.state.enteredTextBoxvalue}
onChange = {this.handleChange}>
there should be only 140 characters
</textarea>
</div>);
}
}
ReactDOM.render(
<TwitterBox name="World" />,
document.getElementById('container')
);
componentWillMount is called right before the component gets rendered.
componentDidMount is called right after the component gets rendered.
If you need to prepare data you use componentWillMount.
componentDidMount is popularly used among sending api calls or grabbing data just right after the component renders and is highly recommended to use that.
componentWillMount:
This function is called right before the component’s first render, so at first glance it appears to be a perfect place to put data fetching logic
componentDidMount:
Using componentDidMount 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.
As part of your question is about performance you could consider also having a look at shouldComponentUpdate to avoid reconciliation.
componentWillMount is invoked immediately before mounting occurs. It is called before render().
componentDidMount is invoked immediately after a component is mounted.
componentWillMount
component is about to render, plays the same role as constructor
there is no component in DOM yet you cannot do anything involving DOM
manipulation
calling setState() synchronously will not trigger
re-render as the component is not rendered yet
I would not recommend calling async /api requests here (technically there is no guaranty they will finish before component will be mounted, in this case the your component will not be re-rendered to apply those data)
componentDidMount
component has been rendered, it already seats in the DOM
you can perform manipulations involving DOM elements here (e.g. initialize third-party plugin)
call async /api requests, etc.

Can I put ajax in React component constructor?

import React from 'react';
class AjaxInConstructor extends React.Component {
constructor() {
super();
this.state = { name: '', age: '' };
this.loadData().then((data) => {
this.setState(data);
});
}
// simulate the AJAX (network I/O)
public loadData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
name: 'slideshowp2',
age: 123,
});
}, 2000);
});
}
public render() {
const { name, age } = this.state;
return (
<div>
<p>Can I init component state async?</p>
<p>name: {name}</p>
<p>age: {age}</p>
</div>
);
}
}
ReactDOM.render(<AjaxInConstructor />, document.body);
Above is my demo code. I know people always put ajax in componentDidMount or componentWillMount lifecycle.
But this case also works.
In chrome console, React throw no error and waring. So, My Question is usage like this is completely correct ? Is there have some error?
You can make an AJAX call wherever you want. There is nothing "wrong" in making an AJAX call in the constructor, but there is a catch. You'll want to make the AJAX call only after the component has been mounted or just before it is about to be mounted.
So before component is rendered, making an AJAX call in componentDidMount() or componentWillMount() is recommended. Just because React allows to do "things" does not mean you should! :)
UPDATE
I also realize that initially my answer wasn't rigorous. I have always followed what fellow programmer have followed, blindly.
After searching a bit I found these to be one step closer to the complete answer-
Why ajax request should be done in componentDidMount in React components?
Essence of those answer says that when you call setState() in componentWillMount(), the component will not re-render. Therefore one must use componentDidMount(). After further reading I learned that it was fixed in subsequent release by React team. You can now call setState() in componentWillMount(). I think that is the reason why everyone recommends making AJAX calls in didMount.
One of the comments also puts forth my thoughts very articulately-
well, you are not calling setState from componentWillMount nor
componentDidMount directly, but from a new async stack. I have no idea
how exactly react is implemented to keep reference to this with live
event listeners from various methods. if using undocumented features
is not scary enough for you and want a bit of excitement that it might
work and maybe even in future versions, then feel free, I don't know
whether it will break or not

How to make a react component that can be rendered with no data and later rendered with data?

I have a situation that I'm sure is common and I just haven't learned the react way for accomplishing it. Let's say I have this:
var appView = new React.createClass({
render: function() {
return (
<div>
<SomeSubview/>
</div>
)
}
});
React.render(
React.createElement(appView),
$('#app').get(0)
);
My question is how should I create the SomeSubView react component so that it can render properly without any data, and then later render showing some data when the data is available. I have pub/sub system set up, so I'd like to be able to subscribe to an event and get the data to SomeSubView that way. SomeSubView might look something like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
// there is no data and renders the data when there is data
return (
<div></div>
)
}
});
I can't tell if this a case for state or props on the react component? I don't know if it's best practice to put conditionals in the render function?
In your SomeSubView just check if data is available in your render function, but before returning the markup.
Like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
if( this.state.data.length > 0 ){
var data = <li>{this.state.data}</li>;
}
return (
<div>{data}</div>
)
}
});
If the variable data is not set, React will simply pass over it as non-existent.
You can of course also use .map() on your state data to loop out markup just like in most render examples.
You have to use state like user3728205 said, especifically setState().
setState(function|object nextState[, function callback])
Merges nextState with the current state. This is the primary method
you use to trigger UI updates from event handlers and server request
callbacks.
The first argument can be an object (containing zero or more keys to
update) or a function (of state and props) that returns an object
containing keys to update.
Here is the simple object usage...
setState({mykey: 'my new value'});
What this says is that "whenever" you update your state via setState, React will execute the method render again for you. So, you should put yor display logic based on the state, when it changes the view displayed will change too.
I say "whenever" because React doesn't fire re-render immediatily, but creates a pending state transition.
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState
and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
For more information about the magic of React you should read this.
https://facebook.github.io/react/docs/reconciliation.html
A simple example that maybe can help.
And i recommend read the flux architecture that is very easy to understand and implement (is about utilizing a unidirectional data flow), and you have implementations like Fluxxor that facilitates the use of flux. This is for your pubsub part.

Event like componentDidUpdate, but fired only once

I have some components that should do some work as soon as their data has arrived and rendered for the first time, but not for future rerenderings. For example: Comments are loaded and rendered, now 1. load social media libraries and 2. load some Google Analytics.
Right now I'm doing it like that:
componentDidUpdate: function (prevProps, prevState) {
if (this.hasAlreadyUpdatedOnce) {
// ... do some stuff...
} else {
// ... do some stuff that should happen only once...
// 1. load social media libraries
// 2. load some Google Analytics
this.hasAlreadyUpdatedOnce = true;
}
}
But I'm asking myself if there's a more elegant way than setting a property like that.
Assuming you're responding to a state change, you should pass a callback as the second argument to setState.
componentDidMount: function(){
ajaxyThing(function(data){
this.setState({data: data}, function(){
// this.state is updated, the component has rerendered
// and the dom is current
});
}.bind(this));
}
You want componentDidMount(). Details here.
Have you tried updating state once the ajax call has finished?
Or you can return false for componentShouldUpdate and once the ajax call promise has resolved call forceUpdate.
I can't give you a definitive answer because I don't know if your ajax call is in the parent or child component but either way you should be able to leverage shouldComponentUpdate() to accomplish your goals. If you really don't ever want to update your component after the ajax call comes in then you can do something like this:
shouldComponentUpdate() {
return false;
}
and then when your ajax call comes back just run this.forceUpdate(). returning false will make it so that your component never updates unless you run this.forceUpdate(). However this is not the best solution to the problem I just can't give a better one without more information.
The React docs have a good example on how to handle this using isMounted().
isMounted() returns true if the component is rendered into the DOM,
false otherwise. You can use this method to guard asynchronous calls
to setState() or forceUpdate().
Example
First, initialize your state variables in `getInitialState()':
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
}
}
In componentDidMount() make the ajax call ($.get in this case) then re-set the state variables:
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
}

Categories

Resources