react state function does it get created on every render - javascript

I understand why useCallback is good. It doesn't create new functions if deps didn't change.
Is the same thing true for state functions ?
for example:
// parent component
const [val, setVal] = useState<boolean>(false);
<childComponent
:setVal={setVal}
/>
Now if parent component re-renders for some other reason than val field, setVal function will be created again, which will cause childComponent to re-render again for no obvious reasons. Is this true ? and if so, I guess, there's no way to work around this ?

No it's fine to pass setVal. useState takes care of returning the same setVal instance on every render cycle.

Related

How to properly pass functions as props to child component in React?

I currently have a function that I want to pass to the a child that is two levels deep. My function is :
const addToTrip= (isChecked, coordinates, title, yelpID)=>{
console.log("added to trip ")
if(isChecked){
setWaypoints((waypoints)=>[...waypoints, {name:title, yelp_id:yelpID, coordinates:coordinates}])
} else if(!isChecked){
const newWaypoints = waypoints.filter((waypoint)=>waypoint.yelp_id !== yelpID)
setWaypoints(newWaypoints)
}
}
My function is used in my Businesses component and it is rendered under certain conditions
{hikes.length>0 && (
<Businesses
hikes = {hikes}
addToTrip = {addToTrip}
/>
)}
When I initially run my app and hikes is empty, I thought my function isnt supposed to be invoked until I call it, but it is being called because my waypoints state gets set twice and my console.log("added to trip ") triggers twice as well.
I tried to go about it with changing my render logic of Businesses to
{hikes.length>0 && (
<Businesses
hikes = {hikes}
// addToTrip = {addToTrip}
addToTrip = {()=>addToTrip}
/>
)}
With the above code, I dont actually get my console log statements and my waypoints state remain untouched. Now the issue with this is I cant seem to use this function in my child component properly like I was able to in my first approach. I know that I am calling the addToTrip function successfully as my console.log will print as expected, but it's not detecting my input parameters any more.
Here is my first child component:
export const Businesses = (props)=>{
const {hikes, addToTrip} = props
return(<>
<div className="businessesColumn">
{hikes.map(hike=>(
<BusinessCard ...
/>
)}
Here is my second child component:
export const BusinessCard = (props)=>{
const {img, title, location, description, star, reviewCount, addToTrip, coordinates, yelpID} = props;
const [isChecked, setIsChecked] = useState(false);
useEffect(()=>{
console.log(isChecked)
addToTrip(isChecked, coordinates, title, yelpID)
},[isChecked])
const handleOnChange = ()=>{
setIsChecked(!isChecked);
}
return (...)
Q1: How come my function is called even when I am not calling it when I am passing it as addToTrip = {addToTrip}? I know react creates the function on every render, unless I usecallback, but is it suppose to "call" it?
Q2: How am I able to pass parameters from child component if I were to pass function as props with addToTrip = {()=>addToTrip}
Q3: Does React create a new addToTrip object for each component it is passed to? Ive noticed that my addToTrip gets called for each component that requires it but doesnt even call it
I greatly appreciate any help.
Q1: How come my function is called even when I am not calling it when I am passing it as addToTrip = {addToTrip}? I know react creates the function on every render, unless I usecallback, but is it suppose to "call" it?
It actually does not trigger your function but passes down to child components only. In this context, you don't need to call useCallback, because your function is never modified.
Q2: How am I able to pass parameters from child component if I were to pass function as props with addToTrip = {()=>addToTrip}
addToTrip = {()=>addToTrip} is not a proper way to call your function. It should be
addToTrip = {()=>addToTrip()} (it will create a new function every time the component gets re-rendered). In this case, you can call useCalback to memoize your function
addToTrip = {addToTrip} (it won't create a new function but re-use the current function)
Q3: Does React create a new addToTrip object for each component it is passed to? Ive noticed that my addToTrip gets called for each component that requires it but doesnt even call it
The problem is from useEffect. When your component get rendered, useEffect will be triggered initially, and addToTrip(isChecked, coordinates, title, yelpID) will get called. For the fix, you should move addToTrip to handleOnChange which is only triggered when you tick on checkboxes
const handleOnChange = ()=>{
setIsChecked(!isChecked);
addToTrip(!isChecked, coordinates, title, yelpID);
}
I can tell in this case that it may not be extremely relevant. But, if you have a huge state tree logic where you would need the same state being passed down from a grandfather component (parent of a parent) to a grandchild (child of a child), then React has a context that helps React JS Context Docs.
For Q1, I believe addToTrip is accurate since you are passing down the function reference (as opposed to passing down something like addToTrip()).

Calling function directly in functional component body in React.js

I have a functional component and some function is called directly in the body (that function changes the state && it's needed to be run each render). Is it 100% okay, or could it cause bugs?
const myFunc = (setMyState, ...) => {
...
setMyState(...)
}
const MyComponent = () => {
const [myState, setMyState] = useState(...)
myFunc(setMyState, ...)
return(<div>some content...</div>)
}
This will cause an infinite loop. This is why:
component renders first time
function updates component state
component renders because of state update
function runs again and updates component again
etc
You can definitely avoid this by using if-statements inside the function, but the best way to update a component state is either by user input/api calls or props updating(your case I suppose)
The best way to do that in my opinion is using the useEffect hook

What are the differences in setting state inside children versus sending as props?

I have got 2 pieces of code one is using independent state in child and the other one with state being send out as props like this in my " sent as props " version.
function App() {
const [isChanged, setIsChanged] = React.useState(false);
const [i, setI] = React.useState(0);
React.useEffect(() => {
console.log("USEEFFECT");
setTimeout(() => {
setIsChanged(true);
}, 2000);
}, []);
return (
<div className="App zx">
{isChanged && <h1>Hello CodeSandbox with outter states</h1>}
<Change set={setI} i={i} />
</div>
);
}
the second one with the exception of having states inside <Change /> as such :
function Change() {
const [i, setI] = React.useState(0);
let rnd = 9;
if (i !== rnd) {
setI(i + 1);
}
console.log(i);
The version in which state is managed inside the child, the component runs twice and I get the log two times in a row but in the one with passed down state as props I get the component running once as desired.
Why is this happening and which one is the correct practice ?
When can I safely use state in a child component without worrying about re-renders ?
Is there a way to avoid the re-render in the first version so I get
the same results despite using state in the child component ? If so please provide me with a sample.
To reproduce,
Props version : https://codesandbox.io/s/heuristic-driscoll-9m1pl
state in child version :
https://codesandbox.io/s/confident-shockley-btjg6
Usually you put the state in the component where you want to use it. For example, if just one child uses the state, put it right in the child component. But let's say you want to use the same state in some different child components, then it's better to have it in the parent component. (in this case, it would be better to use useContext() hook).
Honestly, I don't understand what you want to accomplish with your code but in general, rendering and re-rendering happens when you update your state. The reason it re-render agin and you see 0 to 9 and again 0 to 9 is that your default state is 0 and each time it get's re-rendered, the state changes to 0. (I assume)
Hope this answers some of your questions.

How does this handleClick function in React retrieve previous state value?

This codepen toggles a button value from true to false.
I understand this apart from how this handleClick function is working:
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
Can someone please break this down and explain in simple terms how the function retrieves the isToggleOn bool value?
I know we can't directly use !this.state.isToggleOn in setState but can someone kindly explain in simple terms why this handleClick function is more reliable in this scenario for a React newbie?
This is where the handleClick function is called:
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
From the setState docs:
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied.
In your example prevState is state renamed, and it contains all of the assignments in state, including isToggleOn.
You could alternately just read this.state in the setState function. The prevState pattern is used to communicate that this is the old/current state, the state that is being changed.
setState docs
In this scenario you can actually use both, because it's really simple and you call the setState from the render function. In class components is a bit hard to find a place where this this.setState({oldState:this.state.oldState}) can be problematic.
On the contrary, if you use functional components or hooks, you can see this problem quite often.
JS is not Object oriented!
To understand this you have to think first that js is not an object oriented language. React tries just to simulate it. Basically when you change a state, the class containing it gets called again trying to run only the code which changes. In your case, when handleClick is called, the render function get triggered because the state has changed!
Said this, handleClick is not just a function! It's also a property of the class. React tries to understand whether it should "re-declare" those functions depending on the values they use.
Good Practice
In the case you wrote, isToggleOn: !prevState.isToggleOn, the function is indipendent. It doesn't need any other property of the class (apart of setState, which works with a different pattern), so after the state changes, the react engine doesn't have to set the function again.
Bad Practice (sometimes much much easyer to write)
On the contrary if you use isToggleOn: !this.state.isToggleOn the handleClick function is dependent of this.state so every time it changes, the react engine has to "re-declare" the function by sort of substituting this.state with {isToggleOn:...}
How to recreate that pattern
function myRandom(property){
//Kind of private Variable
let hiddenVar=Math.random();
return property(hiddenVar);
}
//Let's say here the Math class is unreachable while myRandom is.
myRandom((num)=>{
console.log(num);//This ac
})
Maybe just check the docs on this?
The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.
In essence, you are getting the previous instance of the state in the function, then making a change to the state and returning it. This new state will get saved and cause a re-render of your component.
If you use functional components, you can negate having to pass a callback here and could do something like:
function MyComponent() {
const [on, setOn] = React.useState(false)
return (
<button onClick={() => setOn(!on)}>
{on? 'ON' : 'OFF'}
</button>
);
}

Where to store values calculated from props in React

What is the best place to store the result of an expensive calculation from the React props which I use in render() but do not want to execute at each render() ?
constructor(props) {
super(props)
const result = this.doExpensiveCalculation(props)
}
componentWillReceiveProps(nextProps) {
// if nextProps differ from props
const result = this.doExpensiveCalculation(nextProps)
}
doExpensiveCalculation(props) {
// Some expensive stuff
}
render(){
// Use doExpensiveCalculation(this.props) here
}
The options are this and state but both I see rather unsatisfying. Is there a ready solution which uses memoisation?
On the other hand, should I worry about optimizing this ? I read that React can rerender component even if the props have not changed but does this happen often ?
You can handle the re-rendering in the lifecycle method of shouldComponentUpdate. Default value is always return true. By returning false there React will not re-render the component.
See the docs for more. Besides that, React only updates if a state change occurs since props are read-only.
Your options are to store it as you suggested or have a class with a static field to keep it there.
If all you want to do is perform the expensive calculation whenever you get new props, instead of on every render, you probably want componentWillReceiveProps:
componentWillReceiveProps() is invoked before a mounted component receives new props.
As far as where to store them, you can either store them in state, or as a property directly on the component instance. Either will work just as well.
You want to make sure compare values though, to avoid unnecessarily recomputing.
For example:
componentWillReceiveProps(nextProps) {
if (nextProps.someValue !== this.props.someValue) {
this.someResult = this.performExpensiveCalculation(nextProps.someValue);
}
}

Categories