ReactJS - What is difference between component state and class variable? - javascript

I think it works differently but I don't know how it works.
1. Using class variable
export default class Test extends Component {
constructor() {
this.active = false;
}
render() {
this.active = this.props.name === 'Dan'? true : false;
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
2. Using React component state
export default class Test extends Component {
constructor() {
this.state = { active: false };
}
render() {
if(this.props.name === 'Dan') {
this.setState({active: true});
}
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
I think it doesn't need to re-render using State if it's only affected by received props.

The difference between the two is that React will re-render your component when state changes (with this.setState(/*...*/)).
If you update the class variable, React will be unaware of it and won't re-render your component.
Note that what you're achieving in your code requires neither state or class variable. You're simply computing another value directly from the props. A better way to write your component would be like this :
export default class Test extends Component {
render() {
const active = this.props.name === 'Dan';
return (
<div>
{active? 'ssup?' : 'noooo'}
</div>
);
}
}

The simple answer to your question is that by using state you call the setState() which automatically calls render() automatically. Which cannot be obtained by class variables
You use the `state variables` when you want to change the component when that variable is changed.
When you don't want to automatically call `render()` you use the `class` variables

React component only re-renders when there are changes to state or class. But updating class variable involves neither so it does not trigger render.
Though using state may seem similar to class variable but state is a protected keyword in React that refers to stored component data. The major difference between using class variables and state is updating data. Instead of manually reassigning the variable, you call this.setState().
When you call this.setState(). It compares this new state to the previous state. If there is a change, React re-renders the component, resulting in the updated count value displayed on the screen.
But when you update class variable, it sure gets updated but does no re-render. You can do so using this.forceUpdate(). But Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().
Refer to this article for detailed info.

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)
}

How to replace this componentWillReceiveProps?

I have a login and register component with forms that were created a while ago before these methods were deprecated, I've been looking around but cannot seem to find a solution, how would I go about refactoring this to using getDerivedStateFromProps?
componentWillReceiveProps(nextProps) {
if (nextProps.auth.isAuthenticated) {
this.props.history.push("/dashboard")
}
if (nextProps.errors) {
this.setState({
errors: nextProps.errors
});
}
The answer to the question you asked is probably not going to be satisfactory. :-) The answer is that if you really need to derive state from props (you probably don't, just use props.errors directly in render), you do it with the newer getDerivedStateFromProps static method that accepts props and state and (potentially) returns a state update to apply:
static getDerivedStateFromProps(props, state) {
return props.errors ? {errors: props.errors} : null;
}
or with destructuring and without the unused state parameter:
static getDerivedStateFromProps(({errors})) {
return errors ? {errors} : null;
}
But, you're saying "But that doesn't do the authentication thing...?" That's right, it doesn't, because that componentWillReceiveProps shouldn't have, either, it violates the rule props are read-only. So that part shouldn't be there. Instead, if that entry in props.history is supposed to be there, it should be put there by the parent component.
Since you're using componentWillReceiveProps to keep a local state in sync with props you have two alternatives:
Declare your initial state based on props and use componentDidUpdate to ensure props synchronicity
class Component extends React.Component{
state = { foo : this.props.foo }
componentDidUpdate(prevProps){
if(this.props.foo !== prevProps.foo)
this.setState({ foo : prevProps.foo })
}
}
This is actually triggering an extra render everytime, if you have some local state that is always equal to some prop you can use the prop directly instead.
Use getDerivedStateFromProps to update the state based on a prop change, but keep in mind that you probably don't need to use it
class Component extends React.Component{
static getDerivedStateFromProps(props){
return { foo : props.foo }
}
}

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.

How do I Increment a React Prop

Introduction
Hello, I'm trying to build a counter component for my online store, but I'm having trouble getting with it to function correctly.
Problem
From my understanding is that the count value will not increment because it remains in the same instance, and the value is never changed in the parent component just re-rendered. Declaring a variable and trying to increment locally in the addOne() method also is causing the value not to change.
Question
Is there something I'm not understanding about instances and how they work, can anyone give me some suggestions?
class ParentComponentClass extends React.Component {
render() {
const test = "Is anything printing?";
let count = 0;
return (
<div>
<QuantityCounter_CheckoutButton count={count} />
<Test test={test} />
</div>
);
}
}
class QuantityCounter_CheckoutButton extends React.Component {
constructor(props) {
super(props);
this.addOne = this.addOne.bind(this);
}
addOne() {
let qtyCount = this.props.count;
qtyCount++;
console.log(qtyCount);
}
render() {
return (
<div>
<a href="#" id="bazuka_add" onClick={this.addOne}>
<i className="material-icons ">add_circle_outline</i>
</a>
<a href="#" id="bazuka_remove" onClick={this.minusOne}>
<i className="material-icons ">remove_circle_outline</i>
</a>
<p>qty:</p>
</div>
);
}
}
So I know you have marked this as answered but I want to explain this concept to you.This is a simple but very important concept to grab.
Just imagine two functions
function manipulateCounter(count){
count++
console.log(count)
}
function counterContainer(){
let countVariable = 0;
manipulateCounter(countVariable);
manipulateCounter(countVariable);
manipulateCounter(countVariable);
console.log(count);
}
I have a container which calls an incrementer. Now my target is to increment the 'countVariable'. But the way I have done it; my incrementing logic is broken. This is because by passing the 'value' via function I am just passing a copy of the value but not a way to manipulate my variable itself. (If you pass an object it's a whole other story but that deserves a separate blog post)
This is the same with props! You are just incrementing a copy of the values sent to you. Also it gets a little more complicated with react because the 'render(){}' is meant to be something called a pure function. Simply for you this means each time there is a change in react the stuff inside render is destroyed and rerun from scratch. This means you can't maintain a counter variable inside 'render'.
So we have 2 problems.
Find a way to properly maintain a 'state' in the component
Find a way to update this state.
This is where React's component state comes in handy!
class Parent extends React.Component{
constructor(props){
super(props);
this.state ={
count : 0;
}
}
render (){
<Child count={this.state.count}/>
}
}
So now we have found a safe way to maintain the 'state count' in our react component. Next we need a way to update it. This means the child needs to do some up-word communication to the parent since the state is inside the parent component.
class Parent extends React.Component{
constructor(props){
super(props);
this.state ={
count : 0;
}
}
increment(){
this.setState({ counts : this.state.count + 1});
}
render (){
return(<Child count={this.state.count} increment={this.increment.bind(this)}/>);
}
}
This is a very powerful concept in react that allows to build reusable components. The Child doesn't know anything about how the parent manages
the variable. It is only being passed the current value of the count as well as a
function it should call in case the value needs to be incremented.
So in my child I just have to do this...
class Child extends React.Component{
render (){
<div>
<div>{this.props.count}</div>
<button onClick={this.props.increment} >Increment</button>
</div>
}
}
You just have to modify this learning in to your app. Good Luck!
You cannot change a react Prop like this. What you need is probably State
Prop only got passed from parent to the child component and if a prop is going to be changed, it should happen in the parent component.
Check the difference between State and Props here : https://reactjs.org/docs/faq-state.html
I agree about "Check the difference between State and Props here : https://reactjs.org/docs/faq-state.html". But also you should rearrange your components so state and setState will be in one component, because if you don't use Redux the state is local. I would recommend to read this article https://medium.freecodecamp.org/get-pro-with-react-setstate-in-10-minutes-d38251d1c781
By the way, if you use Redux you can leave your layout and set state via reducers. Now you can update your props when state changed using mapStateToProps.

How to set child component state using parent component props in React 16.3? [duplicate]

This question already has answers here:
Reactjs - Setting State from props using setState in child component
(2 answers)
Closed 4 years ago.
I have been working on building an app, where an uncommon scenario has raised for me to handle. I had to pass props to a child component from the parent, and the child component needs to set those props as its state.
<ChildComponent allProps={parentProps}>
<SomeChildren withProps />
</ChildComponent>
In the ChildComponent:
class ChildComponent extends Component {
state: {
someRequiredProps: null
}
setStateForThisComponent(propsToSet) {
this.setState({someRequiredProps: propsToSet});
}
componentDidMount() {
this.props.allProps()
}
render() {
//I know this is incorrect syntax
return (<div allProps={(propsArg) => this.setStateForThisComponent(propsArg)});
}
}
Usually, I can run this method from parent component using refs, but since this ChildComponent being a very heavily reused component, I do not want to go with the refs approach. Some other solutions suggested, that I create the state in parent component itself, and pass it as props to ChildComponent, but that is not what I am trying to achieve.
Please let me solve this issue of setting the child component's state using the parent's.
If you just need to set the initial props as the state, you can do it in constructor:
constructor(props) {
super(props);
this.state = {
someRequiredProps: props.someRequiredProps,
}
}
If the state must be updated whenever the prop updates, you need to use the new getDerivedStateFromProps method:
class ChildComponent extends Component {
state: {
someRequiredProps: null,
};
static getDerivedStateFromProps(nextProps, prevState) {
return {
// return the new state based on `nextProps`
someRequiredProps: nextProps.someRequiredProps,
};
}
Also note from the docs:
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.

Categories

Resources