React props inmmutable vs Props changes in REACT lifecycle - reactjs

I have read alot of documentation about REACT JS and I have found in several sites that .props should be inmmutable... If that's so, Why in the React Component Lifecycle the are some functions that are triggered when the props are changed?
http://busypeoples.github.io/post/react-component-lifecycle/
Can anyone help me understand why?

Props are passed in from your parent. One way to think of them is like a local variable on a stack, in a programming language. You can change them in your code, but that change is only visible in the current component, nowhere else. The next time your parent component renders, those changes will be wiped out.
Re: the React lifecycle,
you're probably thinking of componentWillReceiveProps, which is called when your parent renders. It is fired when new props are passed into an existing component. They will have the same names as existing props, and they may or may not have new values. You can take action in your local state based on when new props come in. You can use it or ignore it, depending on your needs.

its allowed to change props and makes sense in a lot of cases.
the immutability is not meant as u r not allowed to change the prop.
immutability in this context means u should use https://facebook.github.io/immutable-js/docs/#/ when u have deep objects that will be passed as props. of cause u can change the prop but the lib refered to makes sure u always get a complete new object every time you change someting (because the original is immutable ;) ).
the reason u should use a complete new object when the prop changes is so react actually realizes that the prop was changed, because it internaly does no deep compare but just compares the reference to the object. therefore immutable objects as props are the only way to make sure ur changes will be propagated through ur component tree when u make deep changes in ur passed objects/props.

For react, immutablity means more or less:
Once you have passed an object to react as props or in state, you
should never change that same object. Make a copy into a new
object, and change the copy.
React does not have any lifecycle methods that are triggered by a "change in props". React's lifecycle functions will run whenever a higher component passes in a new set of props. Even if the new props are the same as the old props, react will still run all its lifecycle methods.
There are 2 reasons why immutability is important:
Whenever you define new props, react compares the new props with the old props, and decides whether it needs to update the DOM. If they are the same, react will NOT update the DOM (even though all lifecycle methods did run).
Sometimes you want your own code to compare new props with old props (e.g. to change color or whatever). Some lifecycle methods give you access to both new and old props for that. For your code in there to be reliable, you need to make sure that the props do not change after you pass them to react.
Simplified example when things go right:
oldUser = { name: "Bill" } // points to object with name Bill
<Component user={oldUser}/>
newUser = Object.assign(oldUser, { name: "Dave" }) // new object
<Component user={newUser}/>
// newUser != oldUser (different objects):
// react will run render function + update DOM
Example where things go wrong:
oldUser = { name: "Bill" } // points to object with name Bill
<Component user={oldUser}/>
newUser = oldUser // newUser points to the same object as oldUser
newUser.name = "Dave" // Now also oldUser.name == "Dave"
<Component user={newUser}/>
// newUser == oldUser (same objects + both have name "Dave"):
// react will run render function, but will NOT update DOM

Related

React substitute for componentDidReceiveProps

I am creating a chat app and I have a component called ChatBox. ChatBox subscribes to a WebSocket when the user clicks on a chat channel. Right now, this is done in the constructor. The issue is that I now need to let the user change channels, but constructor is not called when the props change. I could use componentDidUpdate, but I would have to compare my props against my old props on every render, which is very inconvenient.
TL; DR: I need to do something when my props get changed. I found a method called componentDidReceiveProps which fits my situation perfectly! But sadly React says that the method is bad practice, and they made it deprecated:
One of the biggest lessons we’ve learned is that some of our legacy
component lifecycles tend to encourage unsafe coding practices ... These lifecycle methods have often
been misunderstood and subtly misused
My question is: what is the correct way to produce the same behavior?
Use componentDidUpudate. Check if the relevant prop changed, and if so, update the socket or do whatever else is needed.
componentDidUpdate(prevProps) {
if (this.props.channel !== prevProps.channel) {
// do stuff
}
}
but I would have to compare my props against my old props on every render, which is very inconvenient.
Yes, you do need to check that the right prop changed. You'd have to do that with componentWillReceiveProps also.
This can be reproduced using the useEffect hook. The if you set the dependency array to contain the prop(s) you are looking for, then it will run only when that prop changes.
To do so, you'll need to convert it to a functional component instead of a class-based component.
Example:
import React from 'react'
Function ComponentName(props){
React.useEffect(() => {
// Code inside here will run when anything in the dependency array changes
}, [prop.propName]) //<---Dependency array. Will watch the prop named "propName" and run the code above if it changes.
return (
//Your JSX
)
}

What's the idea behind useState functionality in ReactJS?

I am learning ReactJS and I understand the benefits of functional programming. However, I am a little concerned about the actual useState approach. If we assume a variable to be changed, then why declaring it const in the first place?
I see that I can simply use let number = 4; and then render it like this <p>{number}</p>. What I cannot do however is to re-render it automatically just by changing it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so? Is there a specific reason I am missing why it was implemented the way it is? I mean why the developers have decided that if the value needs to be re-rendered upon change, then it must be a const value declared with the help of useState functionality?
I am asking this because I am suspecting I am missing some good points behind this and I would like to understand them.
The variable is declared as a const because you are not allowed to set it directly. In React the state itself is immutable. You are just allowed to change it over setState (or setNumber in your case) or with actions if you use redux.
But why is that? It may seem unnecessary cumbersome in the beginning
First of all, if your variable number changes, react has to trigger a rerender.
If the state is mutable, it requires data-binding because if the number is set, it has to update the view.
In javascript, data-binding works for simple objects, but not well for arrays. Vue.js for example, as an alternative that uses two-way data binding, had a lot of trouble in its early versions when dealing with arrays. That's why there are now only seven predefined methods to interact with arrays (which they added later to solve that problem). Vue Js Array Methods
So a simple reason to declare the state as const is that it works better with arrays. And if you watch the example you gave, setNumber(number + 1) is not that much more to write than number++. But setState(newArray) works, and newArray[i] = newElement would not work, because due to javascript limitations this cannot trigger a rerender.
Secondly, it is a nicer design concept. Think of your component as a function, that returns a view to a state. And if the state changes, you get a new view. It simplifies relationships between properties in your component. If you were allowed to change your state while rendering your component, it would create a mess.
The problem is that you're thinking of a functional component as if it was stateful. But it isn't. It's a function and once you run it, that's it.
Take this example:
function useState() {
let value = 1
function setValue(v) {
value = v
}
return [value, setValue]
}
function myFunction () {
const [value, setValue] = useState(); // <----- we use const here
return null
}
Even though we're using const, the value variable only exists within the function, once the function returns that's it. It's the same for components.
The actual value of value is stored in a whole different scope, where useEffect has access to.
Here's a deep dive on how react works internally if you're interested
React works in render cycles, i.e. some state is declared, the DOM (UI) is computed during the "render phase", and then flushed to the actual DOM during the "commit phase".
Within each cycle state is considered constant (const in JS simply means a variable can't be assigned a new value, you could just as easily declare it with let or var instead and react would work the same) but for react's purpose, state is constant during a render cycle. When it is updated via one of the state update methods, react then kicks off another render cycle (update state, compute diff, commit to DOM) and re-renders when necessary.
This process is important and the reason why external state mutations are considered anti-pattern, it goes against the react workflow and leads to buggy code or worse, UI that doesn't update as expected.
React component lifecycle
I cannot do however is to re-render it automatically just by changing
it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so?
React state updates use a process called reconciliation to figure out what changed and what to re-render. In really simplistic terms, when react state is updated it is updated with a new object reference so a shallow object comparison can more quickly detect that the component state updated.
Declaring state and doing ++number simply changes the value but not the reference, and would be considered a state mutation, an anti-pattern in react.

Why are React props immutable?

I've been reading React's Quick Start documentation;
Whether you declare a component as a function or a class, it must never modify its own props
This is a "pure" function, because it doesn't attempt to change its inputs, and always returns the same result for the same inputs:
function sum(a, b) {
return a + b;
}
This in an "impure" function, because it changes its own input:
https://codesandbox.io/s/9z38xv4x7r
function SayHi(props) {
props.name = "Jim"; // TypeError Cannot assign to read only property 'name' of object '#<Object>'
return <h1>Hi {props.name}!</h1>;
}
Why are React props read-only?
A component should manage its own state, but it should not manage its own props. props is essentially "state that is managed by the component owner." That's why props are immutable.
React docs also recommends to treat state as if it's immutable.
That is because by manipulating this.state directly you are circumventing React’s state management, which can be potentially dangerous as calling setState() afterwards may replace the mutation you made.
You may think of React component as a function of its props and state. As you advance through the docs, you'll find that this is the case, as most functions in the React component life cycle have signatures of the form (prop, state) => { //code }.
React docs define props as any arbitrary input given to a component, and the component will render something based on the props ( and sometimes based on state too, if it is a stateful component ). So props is like something that is given to the component for say, reference. Imagine it this way: you are a component, and your parent component gives you a reference book, containing some rules on how you must behave ( a.k.a. render ). Two cases may arise:
You are dumb (stateless): You just read the book, and behave so.
You are smart (stateful): You read the book, and then note some things in your notepad, that you may view, update or delete. You may even take copy down content from the book to your notepad, and then edit the notepad.
Either way, you may not update the reference book given to you. Only the parent component can update it ( example, give you another book, or change its content ).
I don't know if this is a correct representation, but React components work in a similar way. You'll get the hang of it soon. Make sure you read Thinking in React too. Happy coding!
The props of a react component is aimed to store values and functions from its parent component. It's just the pattern, props are immutable. If you want to have a variable that would be mutable, then store it in the state of the component. States are mutable.

PureComponent's shallowCompare is not working as expected

I am trying to avoid unnecessary re-rendering of a child component (Review) by extending it from PureComponenet.
This component is being called from the parent component (Reviews), which is not a PureComponent.
By debugging I am seeing that whenever a change occurs to one of the review, all the reviews related to that product are getting re-rendered. Obviously Reviews' props gets updated and it passes individual Review's relevant props to individual Review components.
where I am also seeing this,
nextProps.reviewer === this.props.reviewer_info -> false
nextProps.reviewer_info = []
this.props.reviewer_info = []
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components? Is that the missing part?
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
Or above 2 are not the correct reasons for the re-rendering in-spite of using pureComponent and I am missing some other part?
Does my parent component also needs to be a PureComponent in order to stop unnecessary rendering of my child components?
No, that's not necessary.
I believe that pureComponent uses shallowCompare and it should return true while comparing 2 empty arrays, is that correct?
It is not. The shallow comparison uses === to compare the old and new value of each property on props and state. But as you can see in a browser console [] === [] is false. This is because each [] expression creates a new array object that is unequal to the others.
As a consequence, a simple array or object literal in a property (e.g. <Review prop={[]}/>) will trigger a rerender of Review whenever Reviews is rendered, negating the effect of the PureComponent.
To enjoy the optimized behavior, you will need to make sure that each non-primitive prop keeps referring to the same object instance as long as its value doesn't change (e.g. by keeping it in Review's state). Furthermore, if there is a change, you will need to create a fresh top-level object for that prop, rather than modify the existing one in-place (so don't use push or assign directly to the prop objects properties.) The immutable-js package can be helpful with this, but es6 spread operators often work fine as well.
My review component has other child components, which I know also should be declared pureComponents. (I will update them but does this stop Review component from achieving pureComponent's benefits?)
It shouldn't affect Review (unless you create the children in the render method of Reviews, like <Review ..>..<Child ../>..</Review>, in which case props.children will change every time and trigger rerendering.)
You can use shouldComponentUpdate() alternatively if your prop/state is an array:
shouldComponentUpdate(nextProps, nextState) {
return JSON.stringify(this.props.reviewer_info) !== JSON.stringify(nextProps.reviewer_info);
}

Using external objects as state properties in React

Suppose I have some external object inside my JavaScript file:
var data = [{id: 1, name:'Test1'}, {id:2, name: 'Test2'}];
which I pass to ReactDOM:
ReactDOM.render(<Test data={data}/>, document.getElementById('container'));
and as a property to the state object for Test component:
getInitialState: function () {
return {localState: data};
},
Somewhere along the chain, I use this:
handleClick: function () {
data[0].id=55;
this.setState({localState: data});
}
which causes re-render. Full code here: http://jsfiddle.net/44ff2j4b/
Is this a good idea? Basically, having external data which will be modified in place in the component and re-rendered appropriately. Are there some side effects of doing this? As far as I'm aware, it's not OK to modify state in the React component, but the "state" here does not belong to a component...it belongs to the domain logic.
In a React component, "state" is very much supposed to be modified, in-fact state of a component can only be modified within it. However, what you are doing here is seeding the state with a prop and then maintaining it internally. So long as you are not duplicating the prop every-time, merely seeding it, it is ok. Do read more about this here. You should however rename the prop from data to initialData to indicate that it will only be used for seeding the state.
Although it may work, it is not generally a good idea to do this.
React likes you to treat props as immutable. So if you pass data as a prop, you are not supposed to change the original data, like you do in:
data[0].id=55;
If you want to pass data to a react component, copy it as initial state in getInitialState(), and change only the copy inside the react component.
NB: for this, you will need to make a deep copy (so not only copy the array of objects, but also copy the objects themselves). ImmutableJS is a solution/ library often used in conjunction with react.
If you also want to change the original data, the proper way is to pass a function as a prop (in addition to the data prop) to the react component. From the react component, you can then call this function, which can then change the original data. The original data change or update should leave your data-copy inside react component intact.
The code (outside react) can then decide whether or not to re-render the entire react component with the new data (but it is standard practice to call ReactDOM.render() only once).

Resources