Why useState hook causes unnecessarily re-render cycle in React? - reactjs

Why does just defining the state like this:
const [flipCardDeg, changeFCDeg] = useState(0);
in a normal function component cause an additional re-render cycle?
Instead of 1 re-render, it re-renders twice.
I know that if somewhere is used "changeFCDeg" to change the state it should re-render, that's OK. But why at the beginning, when initializing everything, it re-renders one more time?
Should I worry about having 2 re-renders instead of one, and if yes, how to deal with it?

React re-renders whenever it detects change. You can try to get control over that by specifying exactly what it perceives as a change. For example, like this:
const getMoreData = false
const [flipCardDeg, changeFCDeg] = useState(0);
useEffect(() => {
console.log('say something once')
return () => {
console.log('why say it again?')
}
}, [getMoreData]) // will only run once unless getMoreData is changed

Related

UseEffect triggering without respect to dependency array

I have a function below which i used as an array dependency to a useEffect handler
const handleInputUpdate = (event) => {
const eventValue = event.target.value;
setState({ ...state, answer_text: eventValue, trigger: true })
// console.log("I am changing for no reason")
}
Below is the useEffect handler
useEffect(() => console.log(" I am changing for no reason in useeffect"), [handleInputUpdate])
What i want is the useEffect handler to run only when the handleInputUpdate function is called but it runs also on component mount.
Here's what i've observed
The handleInputUpdate function doesn't run on component mount but only i need it to
Without respect to the above observation, the useEffect handler runs anyway.
Here's what i've tried
I tried consoling a text inside the handleInputUpdate function to see whether it runs on component render but it doesn't.
Even though the function doesn't run, the useEffect handler triggers anyway which is not what i want.
How can i solve this ?
Thanks in advance
useEffect dependency array is not used to trigger the effect when a function is called; the elements of the array are observed for any change and then trigger the effect.
In this case, handleInputUpdate will change on every render because it is not memoised, so the effect will also run on every render.
Since handleInputUpdate changes the state when it is called, you are better off adding that state to your useEffect dependency array:
useEffect(() => {
if (answer_text && trigger) {
console.log("I am changing for a reason in useeffect")
}
}, [answer_text, trigger])
The handleInputUpdate function, while it doesn't run on render, looks like it's created when the component runs, just before rendering. Since it won't be === to the value last in the dependency array - the handleInputUpdate from the prior render - the effect callback will run.
You need to observe changes to the answer_text value in state instead.
useEffect(() => {
// ...
}, [state.answer_text]);
I would also recommend separating out your state into different variables - these aren't class components, don't feel like you have to mash everything together into a single object structure.
const [text, setText] = useState('');

useCallback vs useEffect in React

What's the different between useEffect when you pass it dependencies as the second parameter and useCallback?
Don't both essentially run the function/code passed as the first parameter whenever the dependencies passed as the second parameter change?
From what I've read the two hooks are intended to serve different purposes, but my question is whether they in actuality could be used interchangeably because they functionally do the same thing
They're too different.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
You can't switch useEffect with useCallback alone because you also need the logic to run the newly created function. (I suppose you could implement this if you used a ref as well, but that'd be quite strange.)
You can't switch useCallback with useEffect because you very often don't want to run the newly created function immediately - rather, you usually want to pass it as a prop to some other component.
useCallback primarily exists for optimization purposes, to reduce re-renders of a child component.
No, They are not same.
useEffect - is used to run side effects in the component when something changes. useEffect does
not return you anything. It just runs a piece of code in the component.
useCallback - Whereas useCallback returns a function, it does not execute the code actually. It is important to understand that
functions are objects in Javascript. If you don't use useCallback, the function you define inside the component is
re-created whenever the component rebuilds.
Example
Consider this example, this component will go in a infinite loop. Think Why?
const TestComponent = props => {
const testFunction = () => {
// does something.
};
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
Because on each render the testFunction
would be re-created and we already know that ueEffect will run the code when ever the testFunction changes. And since testFunction changes on each render, the useEffect will keep on running, and hence an infinite loop.
To fix this, we have to tell react, hey please don't re-create the testFunction on each render, create it only on first render (or when something changes on which it depends).
const TestComponent = props => {
const testFunction = useCallback(() => {
// does something.
}, []);
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
This won't be a infinite loop, since instance of testFunction will change only on first render and hence useEffect will run only once.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
Let's take an example, If I run the below code and click the first button it'll always rerender MemoComponent as well. Why because every time
we are passing new onClick function to this. To avoid re-rendering of MemoComponent what we can do is wrap onClick to useCallback. Whenever you want to create a new function pass state to the dependence array.
If you want to perform some action on state change you can write inside useEffect.
const Button = ({ onClick }) => {
console.log("Render");
return <button onClick={onClick}>Click</button>;
};
const MemoComponent = React.memo(Button);
export default function Home() {
const [state, setState] = useState(1);
useEffect(() => {
console.log(state); // this will execute when state changes
}, [state]);
const onClick = () => {};
// const onClick = useCallback(() => {},[])
return (
<main>
<button onClick={() => setState(1 + state)}>{state}</button>
<MemoComponent onClick={onClick} />
</main>
);
}
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
OR
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, []);
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change. You should use it when a function is a dependency of a side effect e.g. useEffect.
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.

When updating state from props, why useEffect() instead of checking directly in the component?

When updating state when props change, the commonly held approach is to use useEffect() with the prop as a dependency:
const Component = ({prop}) => {
const [state, setState] = useState(prop);
useEffect(() => {
setState(prop);
}, [prop]);
return <div>{state}</div>;
}
I have to wonder, is there an advantage to this over doing a comparison directly in the component itself, like so:
const Component = ({prop}) => {
const [state, setState] = useState(prop);
if (prop !== state) {
setState(prop);
}
return <div>{state}</div>;
}
It looks like both approaches cause the component to execute twice -- once with the prop and state out of sync, once in sync -- but the second approach looks like it avoids adding a hook to the stack. It also looks like it could be optimized out of the DOM reconciliation, since it doesn't have to wait for the DOM to be generated the way useEffect() does. I'm not even sure it is easier to read, other than being "The Hooks Way."
Does anyone have an idea why the useEffect() route could be better than the inline check?
The official React docs use the second approach for syncing props to state:
https://reactjs.org/docs/hooks-faq.html#how-do-i-implement-getderivedstatefromprops
function ScrollView({row}) {
const [isScrollingDown, setIsScrollingDown] = useState(false);
const [prevRow, setPrevRow] = useState(null);
if (row !== prevRow) {
// Row changed since last render. Update isScrollingDown.
setIsScrollingDown(prevRow !== null && row > prevRow);
setPrevRow(row);
}
return `Scrolling down: ${isScrollingDown}`;
}
The difference between updating state in useEffect and updating state during render is that, useEffect is called after React already commits the updates, i.e. the updates would be reflected in DOM, then you'll update state which will update the DOM again. The second way causes a re-render, but there's only one commit to the DOM.
I think it's important to understand when to use hooks and when not too.
The answer to this question is a very helpful How does React implement hooks so that they rely on call order.
We have recently discovered a lot of problems to do with the useEffect hook, one of them being
useEffect(() => {
// Called on first render and every props.foo update
}, [props.foo])
We didn't want it to be called on the first render only on every update.
Another on being useEffect gives warning when using objects & arrays, instead of a value.
I'm not saying don't use the useEffect ever I love the useEffect hook, but instead of saying why shouldn't use it. You should say do I need useEffect to get what I need to achieve.
For your example unless you are setting the state at another point I would suggest. Just using the prop. If you are setting the state I would have a look at the above link at the schema found.
The schema of a single hook is as below. It can be found in the implementation
function createHook(): Hook {
return {
memoizedState: null,
baseState: null,
queue: null,
baseUpdate: null,
next: null,
};
}
Think do I need to make use of the hooks queue when setting the state, if not then don't use a hook.
Another good use of the hook is are you going to be putting multiple values in the hook array if so it's a good idea to use it then!
Hope that helps :)

Updating state with Hook causes infinite loop within function

I'd like to understand better why updating the state within a function that I call directly below is causing the function to run in a infinite loop.
I understand that I should be using "useEffect" if I want to update the state immediately after it mounts, but I want to know the mechanics behind why this infinite loop is happening.
I have tried removing the function call that updates the state, and I can see that it no longer runs in an infinite loop.
import React, { useState } from "react";
const App = () => {
const [tasks, setTasks] = useState();
const getSheetData = text => {
setTasks(text);
console.log(text);
};
getSheetData("time");
If "setTasks(text)" is removed, there is no longer an infinite loop present.
When state is updated, the component re-renders. So, in your case, you are updating the state, which re-renders the component, and then you are updating again - causing an infinite loop.
React.useEffect(() => {
getSheetData("time")
}, [])
The [] as the second argument means the dependency array is empty - causing it to happen only once (similar to componentDidMount())

Multiple state changes from multiple effect hooks

I can't seem to find a good pattern for one scenario...
Lets say we have this kind of order in component:
const component = ({propslist}) => {
const [state1, changeState1] = useState();
const [state2, changeState2] = useState();
useEffect(() => {
//this effect does something and updates state 1
const someVar = someOperation();
changeState1(someVar);
});
useEffect(() => {
//this effect does something and updates state 2
const someVar = someOtherOperation();
changeState2(someVar);
});
return (<div>...</div>);
}
Now, if i understand correctly and from what i see in my tests, the moment first useEffect changes the state, the component will re-render.
The thing that makes me think so is that if i put it that way i get error: Rendered fewer hooks than expected.
2 questions:
Is it the case that the moment something changes the state that component stops execution and goes into re-render?
How to change multiple states from multiple effects? Is there some good pattern about it? Should we have remodel things to pack all state changes into single effects hook or pack all 'chunks' into single state monolith object and change it from single place?
Any suggestions & best practices would be appreciated.
[UPDATE]
My apologies.
I was testing different versions and posted wrong code example.
This is the code example that causes error Rendered fewer hooks than expected.:
const component = ({propslist}) => {
const [state1, changeState1] = useState();
const [state2, changeState2] = useState();
if(someCondition)
changeState1(something);
useEffect(() => {
//this effect does something and updates state 2
const someVar = someOperation();
changeState2(someVar);
});
return (<div>...</div>);
}
So, i guess call to changeState1() starts re-render immediately and prevents useEffect from being called thus causing the error. Right?
To avoid the "Rendered fewer hooks than expected" error, you need to put your useEffect hooks after the if statement.

Resources