Why do we need to use setValue in UseState? - reactjs

So, React has UseState hook, and we should use it like this.
const [value, setValue] = useState(0);
And if we want to change a value, it should be written like this:
const increase = () => {
setValue(value + 1)
}
Why it's not possible to write it like this? Why we need setValue?
const increase = () => {
return value + 1;
}
I understand that it just doesn't work, but I couldn't find an explanation why it is like that.

The useState hook returns a function to update the state. Using setValue(value) method, you can get the previous(or old) state value to update the state.
On the other hand, without useState when you tries to increment the value, the value will not change because the react rendered the component only once and since there is no state change it won't get re-rendered, the value will remain at 0.
You can see here in details : https://dev.to/aasthapandey/why-to-use-usestate-in-react-pkf

Answer is simple...! The state variable is directly linked to rendering your page. The state variable of useState hook is read only for the component to render it in the below return statement which renders the JSX.
The setValue() function not only changes the state value, but will also trigger the react to re-render the DOM which has already being painted on the browser.
Well that's a difference between an ordinary HTML page & a React App. React provides amazing framework to re-render the page automatically after the you alter the state using the setValue() fucntion

Related

Why doesn't my state change when the category on useParams changes?

const { category } = useParams();
const [sort, setSort] = useState(category);
This think is really driving me crazy - when i console the category, it is actually changing, but sort state doesn't..
You need to do some reading in order to understand how React Hooks work. There are many tutorials online - the React API documentation is even a good start:
What do we pass to useState as an argument? The only argument to the useState() Hook is the initial state.
From the React docs. Notice they say the argument is the initial state only.
When you do useState(X) - the value X is only used the first time the component is rendered. On subsequent renders, if and when X changes, and you want to use the new X, you must update the state value yourself via the set function returned by useState.
In your case, we can say that the state variable changes whenever category changes, so category is a dependency of the state variable. When you have a dependency of this kind, you know you need to use an effect, by way of the useEffect hook:
const { category } = useParams();
const [sort, setSort] = useState(category);
// This callback will run whenever the dependencies
// specified in the second argument change.
useEffect(() => {
setSort(category)
}, [category]) // <= the dependencies
You are expecting whenever category changes, sort should also changes, if this is the goal, try this:
useEffect(() => setSort(category), [category]);
if you only want to change sort first time on initial render, try this:
useEffect(() => setSort(category), []);
For more info, see Using the Effect Hook - React

How to force an update for a functional component?

I'm learning redux and want to find out how useSelector updates a component, because the component doesn't have its own state.
I understand that useSelector() subscribes the component to the store, and when the store is updated, the component also updates.
Class components have this.forceUpdate(), but functional components don't have it.
How does one force-update a functional component?
You can simply do this
Add a dummy state that you can change to reliably initiate a re-render.
const [rerender, setRerender] = useState(false);
...
//And whenever you want to re-render, you can do this
setRerender(!rerender);
And this will re-render the component, since components always re-render on state change
The react-redux package relies on the rendering engine of react/react-dom to trigger the re-render of a given component that uses the useSelector hook.
If you take a look at the source of useSelector you can notice the use of useReducer:
const [, forceRender] = useReducer((s) => s + 1, 0)
As the name (forceRender) implies, redux uses this to trigger a re-render by react.
With v8 of react-redux the implementation of this mechanism changes but still relies on react-hooks for the re-render.
If you are curious how React handles re-renders, take a look at this excellent SO answer. It provides a great entry on the implementation details of how react-hooks are associated with the calling component.
I don't repeat Ryan here, but to sum it up:
The renderer keeps a reference to the component that is currently rendered. All hooks being executed during this render (no matter how deeply nested in custom-hooks they are) ultimately belong to this component.
So, the useReducer is associated with the component within which you called useSelector.
The dispatch function of useReducer triggers a re-render of this component (React either calls the render() method of a class-component or executes the function body of a functional component).
If you are curious how react-redux determines when it should force this re-render (by utilizing useReducer), take another look at the source code of useSelector.
Redux uses the subscriber-pattern to get notified of updates to the state. If the root-state of redux is updated the following things happen:
useSelector hooks in your application re-run their selector function
This re-selected state is compared to the previously selected state (by default via === comparison). The second argument to useSelector can be a comparison function to change this behavior
If the re-selected state differs from the previously selected state, a re-render is triggered via the useReducer hook.
The subscriber pattern is very react-like but potentially helps save many re-renders. Calling several useSelector hooks is cheap when compared with re-renders.
First of all, I want to mention that you don't need to do a force update when you use useSelector hook. Rerender will happen automatically whenever the selected state value will be updated.
But if you need to force update the functional component you can use this approach.
import React, { useState } from 'react';
//create your forceUpdate hook
function useForceUpdate(){
const [value, setValue] = useState(0); // integer state
return () => setValue(value => ++value); // update the state to force render
}
function MyComponent() {
// call your hook here
const forceUpdate = useForceUpdate();
return (
<div>
{/*Clicking on the button will force to re-render like force update does */}
<button onClick={forceUpdate}>
Click to re-render
</button>
</div>
);
}
I highly recommend avoiding the use of this hack, in 99% of issues you can resolve them without force update. But in any case, it's good to know that there is such a possibility in the functional component exists too.
Maybe something like this could help you:
In a Class Component you could pass a property like the one below...
<Element onSomethingHappen={
()=>{
if(shouldComponentUpdate())
this.forceUpdate();
}}/>
In the function component you can call the updater like this one:
function FunctionComponent(props){
//When you need it you can update like this one...
props.onSomethingHappen();
// Here you are ;) let me know if this helps you
}
Continuing on other answers, to keep your code clean you can create a dummy state and then set it in your own forceUpdate function:
const [helper, setHelper] = useState(false);
function forceUpdate(){
setHelper(!helper);
}
Now you can just call forceUpdate() in the rest of your code:
<div onClick={() => forceUpdate()} />

useContext value changes on rerenders

So I have a component that looks something like
const App = () => {
const someContextValue = useSomeContext(); //custom hook that calls useContext
useEffect(() => {
someContextValue()
}, [someContextValue]);
return <div />
}
Whenever the component rerenders, the useEffect is triggered even though someContextValue hasn't really changed.
I got around this by using useMemo like
const someContextValue = useMemo(useSomeContext, [useSomeContext])
Now someContextValue doesn't change on rerenders. But I feel like this isn't quite right. What would be the right way to do this?
If you're returning an object {} or array [] from the context, then the context value HAS changed.
The someContextValue variable is defined inside the component.
The component is a function, and when a function runs, the values that are defined inside it get defined. If for example, your context returns an object containing state values, then that object is a different instance to the one from the previous render, and your useEffect runs because of that.
useEffect will compare reference-types by their instance and not the values inside them.
When you call useSomeContext(), you're creating a new object, and this happens every time the component renders.
This is not the same as a state value from useState, where the state value does not get redefined. The useState hook maintains the same instance of the value, therefore it is not recreated every time, and the useEffect sees that the state value is the same instance.
This is why, when using context, you should destructure the context object and refererence the values inside the object, which are either state values passed from a useState hook inside the context, or a value defined inside the context that does not get redefined when your consuming component re-renders (because the context does not re-render in that case):
const { someStateValue } = useSomeContext()
useEffect(() => {
someStateValue()
}, [someStateValue]);

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

Passed-in prop used to initialize a useState hook gets updated when calling the hook's updater method

Given the following code:
const MyComponent = ({ myObj }) => {
const [myStatefulObj, setMyStatefulObj] = useState(myObj.subObj)
const handleChange = () => {
const updatedObj = { newProperty: Math.random() }
setMyStatefulObj(updatedObj) // myObj is updated too!
}
return <div />
}
I am trying to initialize a new local prop with useState, and hydrating its initial value with a prop that is passed in from a parent component.
When I update my prop using setMyStatefulObj everything works fine, except that it's also updating the prop that's passed into the component.
This is an undesired side effect and one I haven't come across before.
The prop that is getting passed in is from a parent that creates it with a selector from react-redux's useSelector hook.
There are no actions being dispatched or anything, but simply logging the value of the myObj prop shows that it is indeed being updated every time the local setMyStatefulObj method gets called.
The effect you are describing doesn't make sense and I was not able to reproduce it. Maybe you could create a snippet and share it.
I don't think that was caused by the hooks function.
I have created a simple example in CodeSandBox as you said, and it obviously not reproduced. You can edit this sandbox and try to simulate your situation maybe you can find the root cause.
But since you are using redux, I would suggest you to use useSelector to select the state you need inside hooks function, then you would not have this kind of concern, and it will have better performance(if you pass the prop, every time myObj changes the hooks component will re-render).

Resources