React js state and lifecycle - javascript

componentDidMount() {
const user = auth.getCurrentUser();
this.setState({ user });
}
I have this code, I think this.setState({ user });takes a little time, if I want to do some checks immidetly like
<Route
path="/foo"
render={props =>
this.state.user ? (
<Bar {...props} />
) : (
<Redirect to="/login" />
)
}
/>
when refreshing the page the user at the beginning is always null. what is the proper solution? do I need to set state at constructor? or did I do something wrong?
My account got blocked by some down votes questions, the funny thing is I have to re-edit them, even though I already have the accepted answer.I do not understand what's the point to do this.I am so frustrated by this stackoverflow system.
Now, I basically can do nothing but keep editing my questions, and they have all been answered. This is ridiculous !!!

Yes, you should initialise your state in the constructor.
See the React docs example:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}

when refreshing the page the user at the beginning is always null
#Shubham Khatri did explain it really well, in short, just because the render() function is called before the componentDidMount(), hence, the user is always null.
Take a look at this: React lifecycle methods diagram
And as you can see that, the proper place for setState should be the contructor() cuz it's called before the render().
However, for api calls why componentDidMount is the better place? why
we do not do all set up in constructor?
I know you're talking about this: https://reactjs.org/docs/faq-ajax.html . The document does say that: You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.
However, in another place, they say:
You may call setState() immediately in componentDidMount(). It will
trigger an extra rendering, but it will happen before the browser
updates the screen. This guarantees that even though the render() will
be called twice in this case, the user won’t see the intermediate
state. Use this pattern with caution because it often causes
performance issues. In most cases, you should be able to assign the
initial state in the constructor() instead. It can, however, be
necessary for cases like modals and tooltips when you need to measure
a DOM node before rendering something that depends on its size or
position.
...and also for the case that you need to authenticate because this process depends on the value of the user ( as your design).

The problem in your code is that, componentDidMount is called after render and by the time your user details are fetched and stored in state, your component is ready to redirect to /login since user wan't available. To solve this issue, you need to fetch user details before the initial render and hence constructor is the ideal place to do it
constructor(props) {
super(props);
this.state = {
user: auth.getCurrentUser()
}
}

The state goes inside the constructor, but only if you need a constructor (e.g.:to initialize flags). If you don't need a constructor you can just initialize the state outside:
class MyComponent extends Component {
state = { myState = [] }
render() {
const { myState } = this.state
return (...)
}
}

You should use the constructor() to initiate state, componentDidMount() for call functions and componentWillReceiveProps() for setState.
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: ""
};
}
componentDidMount() {
this.props.userProfile();
}
componentWillReceiveProps(nextProps, prevProps) {
this.setState({
firstName: nextProps.user.firstName,
lastName: nextProps.user.lastName
});
}

Related

Why I am getting the old state values after the props change

I just want to understand why in the application I have following situation, below is my constructor of class component:
constructor(props) {
super(props);
this.state = {
tableAlerts: props.householdAlerts,
initialAlerts: props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}
in render function I have:
const { householdAlerts } = this.props;
My issue is that in constructor I got empty array, but in render funtion I have the data. Is it possible to get the data in constructor?
This is a very bad pattern when using the class component. You are ignoring any props updates when you copy the value into state. to manage it:
It requires you to manage two sources of data for the same variable: state and props. Thus, you need to add another render each time your prop change by setting it into state (don't forget to test on equality from prev and next values to avoid being in an infinite loop).
You can avoid setting the state each time your props change by using the getderivedstatefromprops lifecycle method.
So the recommendation is: just use the props; do not copy props into state.
To learn more why you shouldn't, I highly recommend this article.
It is not recommended to set your initial component state in the constructor like so because you gonna lose the ability to use { setState } method after to update this property/state.
The best practice is indeed to refer directly to the prop with { this.prop.householdAlerts }, and keep the state usage for local (or in child components} cases.
if anyhow you want to store props in component state for some reason, call it in lifeCycle -
componentDidMount() {
const { tableAlerts, initialAlerts } = this.props;
this.setState({ tableAlerts, initialAlerts });
}
Hagai Harari is right. Nevertheless, your actual problem seems to be that during your initial rendering the array is empty. Can you ensure that the array has some items, when your component is rendered for the first time?
First rendering -> calls constructor
<YourComponent householdAlerts={[]} />
Second rendering -> updates component
<YourComponent householdAlerts={[alert1, alert2, alert3]} />
If you want initial state to have the prop value.Try something like this with 'this' keyword
constructor(props) {
super(props);
this.state = {
tableAlerts: this.props.householdAlerts,
initialAlerts: this.props.householdAlerts
}
console.log('householdAlerts', props.householdAlerts)
}

why do we need to have component state variables inside predefined state object of component when we can just call setState({}) to re-render the page

In all react tutorials that i have seen, variables which are responsible for changing the content of the component are kept in state object and calling setState() method updates those variables and re-renders the page. From further reading it seemed that only function of this setState() method was to re-render the page
My Question is if setState() is just used for re-rendering why not just keep the state object empty, declare all your dynamic content changing variables as class attributes like you normally do and call this.setState({}) whenever you want to re-render.
So what is the use of keeping variables inside state object? When does above technique fail?
example instead of:
class Button1 extends Component {
constructor() {
super();
this.state = {
counter: 0,
};
}
incrementCounter = () => {
this.setState({counter:this.state.counter+1,message:""})
};
render() {
return (
<div>
<button onClick={()=>{this.incrementCounter()}}> Click </button>
<p>{this.state.counter}</p>
</div>
);
}
}
i could do:
class Button2 extends Component {
constructor() {
super();
this.counter=0;
}
refresh(){
this.setState({});
}
incrementCounter = () => {
this.counter+=1;
this.refresh();
};
render() {
return (
<div>
<button onClick={()=>{this.incrementCounter()}}> Click </button>
<p>{this.counter}</p>
</div>
);
}
}
i personally like 2nd approach? What are its pitfalls?
calling setState does not simply just re-render the page, it rerenders if it notices that something changed on the screen that needs a rerender and rerenders that certain part which makes it much more efficient than just rerendering the whole page constantly.
The short answer is Yes! you can just setState() when you need a new state variable,
But it's dirty, you might define ten's of states in your component and after a while it will keep getting harder and harder to read your code,
The best approach is to define a state={...} in the root of your component or this.state={...} in the constructor and initiate ALL your needed state variables there.
This way, your code will be easy to read and maintain regardless of how big is your component.
You could set any constant variables like that, you just can't change them later with setState and rerender the component. State is meant for variables that are to be changed during the life cycle of the component.

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.

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/

Updating view from setState() not triggering rerender when expected

Answer:
I'm a bonehead. This is way late but don't want to leave a thread unanswered and especially since my initial answer was wrong. I needed to reference the new props I was getting, not this.props. As usual the answer was in the documentation. I updated my own answer below to reflect this.
Edit 1:
My first fiddle did not fully show my issue so I've updated it to demonstrate better. For some reason when I call my setState() I believe the first pass through it is undefined even though its given a value, yet on subsequent passes it works as expected. It seems like my initial setState() call is not triggering a rerender but all others are.
A bit different to the usual "setState not updating view" question as setState() IS updating my view and with the correct value. Just not when I expect it to. Basically I am triggering a setState() event should rerender my child component with new props which I believe should trigger the childs components componentWillReceiveProps lifecyle event, which will then call setState() within the child component and update its view. The issue is while it does update the view, it seems to do it a cycle behind when expected. In my MVP I call setState() at 2 seconds yet it updates the view at 4 seconds. I haven't been able to determine which part is the bad logic though.
Here is my jsFiddle MVP. Thanks for any suggestions.
Code:
class TaskBody extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillReceiveProps() {
this.setState({
activeTask: this.props.activeTask
});
}
render() {
return <div>
< h4 > {
this.state.activeTask ? this.state.activeTask : 'Task Title'
} < /h4>
< /div > ;
}
}
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
activeTask: '',
counter: 1
};
this.updateActive = this.updateActive.bind(this);
}
updateActive(task) {
this.setState({
activeTask: task
});
}
componentDidMount(){
var self = this;
setInterval(function(){
if(self.state.counter === 4){
clearInterval(self.clickInterval);
return clearInterval(self.countInterval);
}
self.setState({ counter: self.state.counter + 1 });
}, 1000);
// imagine this was the click event, it should rerender the view
// instantaneously at 2 seconds because I called setState() right?
// which is supposed to trigger a re-render and then TaskBody should
// receive new props triggering it to setState() on its activeTask,
// which should update the view?
self.clickInterval = setInterval(function(){
self.setState({ activeTask: 'took double the time it should' });
}, 2000);
}
render() {
return <div>
< TaskBody activeTask = {
this.state.activeTask
}/>
<div>{this.state.counter}</div>
</div>;
}
}
ReactDOM.render(<Body />, document.querySelector('#body'));
The
self.setState({ activeTask: 'took double the time it should' });
is never actually called.
Here's why:
Your logic is located within the componentDidMount lifecycle method. If you read the documentation of componentDidMount, you'll see that it clearly states:
Invoked once, only on the client (not on the server), immediately
after the initial rendering occurs.
So, when componentDidMount gets called in your app, you're first checking the counter and calling a setState there. That alone will trigger a new render. This means that your second code block within componentDidMount is going to have no effect because while you set the method, it's never going to get called anywhere.
In my old answer, I was using a setTimeout() as a hack to get the results I wanted. Basically, if I wrapped my setState in a timeout it would set the state with the new props, but if I did not it would still reference the old props. Fortunately, this is already handled in react as componentWillReceiveProps already receives a newProps argument by default.
This:
componentWillReceiveProps(){
var self = this;
setTimeout(function(){
self.setState({activeTask: self.props.activeTask});
},0);
}
becomes
componentWillReceiveProps(newProps){
var self = this;
self.setState({activeTask: newProps.activeTask});
}

Categories

Resources