I am switching from using class components into using functional components in React. I read through their documentation and I like to know how the useState hook work.
In their FAQ they the same question that I'm asking, by saying that each component have a internal list: for storing the state variables, and that the useState know the calling component for storing the state variable.
I want to know how does useState know the calling component since we don't pass it anything other than the initial state value.
Can someone explain me this with a example? I really want to know how this work.
In short, at the begining of render a component, React generate a value like currentComponentContext. When you call useState, React will bind this state with currentComponentContext.
see function mountState and dispatchAction in ReactFiberHooks.new.js, react bind the state with currentlyRenderingFiber.
useState is the state hook provided by react. It returns a “pair” the current state value and a function to update it and also takes a single argument which is the initial value that you want to assign for the state variable.
In the above example count is the state variable and setCount is the function for changing the value and 0 is the default value we are assigning to count. In this example we used a counter as integer value just for demonstration, but we can use any other type like string, object, array etc also we can use multiple state variables defined in a single component.
import React, { useState } from 'react;
const MyComponent = (props) => {
const [count, setCount] = useState(0)
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment Count
</button>
<button onClick={() => setCount(count - 1)}>
Decrement Count
</button>
</div>
)
}
export const MyComponent;
Assuming that seterInClass is an arrow function define to set the value of the variable username.
setterInClass = (val) =>{
this.setState({username:val});
}
in a class the initialization is through this.state
in a functional it is through the userState which acts as a glue.
this.state = {username:''} + setterInClass is equivalent to const [username, setUsername] = useState('').
Meaning useState is establishing a relation of setter to the variable username, after initializing it; And avoid us to write this.state to get the value of any variable or having top write const {username} = this.state;
So it is cleaner, less code for us and make functionals component looks cute
"useState" returns an array with two items. In the first index you have the state itself and in the second index you will get a function to handle that state.
Do this and you will understand :
const useStateArray = useState();
console.log(useStateArray[0]);
console.log(useStateArray[1]);
Related
I'm trying to render the string array keys into a React component. keys are the keys that the user presses (but I just hard-coded them for the sake of this example).
import { useState } from "react";
import * as ReactDOM from "react-dom";
let keys = ["a", "b"];
function App() {
let [keysState, setKeysState] = useState([]);
setKeysState((keysState = keys));
return (
<div>
{keysState.map((key) => (
<li>{key}</li>
))}{" "}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.createRoot(rootElement).render(<App />);
But I'm getting this error:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
I know I can avoid this error by creating and onClick handler ... but I don't want to display keysState on click. I want it to display and re-render immediately when keys changes.
Live code: https://codesandbox.io/s/react-18-with-createroot-forked-vgkeiu?file=/src/index.js:0-504
when the page loads, the setKeysState function gets invoked and it updates the state, updating the state in reactjs causes a re-render and it keeps doing that infinitely. which produces Too many re-renders error.
just pass an initial value to the useState() hook to initialize the state. like this :
let [keysState, setKeysState] = useState(keys);
NOTE : In your case You do not need The React useState Hook because you're not tracking the data (keys in your case, you are not be updating it )
just change your component like this :
let keys = ["a", "b"];
function App() {
return (
<div>
{keys?.map((key) => (
<li>{key}</li>
))}
</div>
);
}
While #monim's answer is great, I feel that you don't need a useState hook if you don't need setKeysState
import * as ReactDOM from "react-dom";
let keys = ["a", "b"];
function App() {
return (
<div>
{keys.map((key) => (
<li>{key}</li>
))}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.createRoot(rootElement).render(<App />);
EDIT: I put together a series of links that I did not want to lose. If you want to deep dive, here's what I personally saved on the hooks: https://github.com/criszz77/react-js-links#hooks
There seems to be a bit of confusion as to how useState works.
You are getting infinite refreshes, because the component re-renders if there is a change to a state. And what is happening there is, that you component loads, you init your state and set value, which triggers re-render and that runs in loop. To solve it just set initial value to keys instead of []
Things that no one mentioned
Make states constants as there is, I think, never a good reason to change the fact that it's a state and if you want to change the value you use setKeysState
setKeysState is a function where that you call with the new value of keysState, so do never change value of that state by anything else but setKeysState(newValue). You are passing something that I'd describe as ?function?. Just pass the value.
Solution:
const [keysState, setKeysState] = useState(keys);
Many different problems with this code, and this allow me to tell you that you haven't got the way of React hooks yet.
First of all, you don't need to write this setKeyState function. The useState hook will return it when invoked.
Second, if you want to provide an initial value to your keyState, you should do it using the setState hook, just like this:
const [keyState, setKeyState] = useState(["a","b"]);
This would create the keyState variable and initialize it with ["a","b"], as well as provide the setKeyState function.
I believe this is the main problem with this code. This redundance is causing the perpetual re-render.
Finally, you should have some way to react to state changes. This will be given by the useEffect hook.
useEffect(() => {
// Things to perform when `stateKeys` changes
},[stateKeys])
The second parameter this hook receives [stateKeys] is exactly to prevent a perpetual re-rendering. It tells the hook it should run only when the variables inside this array changes.
It seems to me, allow please don't be offended for me to say, that you missed something in React way of doing things. Specially when it comes to React Hooks. I suggest you to read the documentation again:
https://reactjs.org/docs/hooks-state.html
I know that in React class components React uses the "this" binding to "track" props and state. In hooks it is achieved thanks to closures, is my doubt?
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
This code works as you would expect a counter, if you were using class component you would have to use setState() in callback mode to access the previous state, but in hooks how does the magic happen?
are there any bindings on the hooks or not?
React keeps track of state by internally counting the number of times useState is called the first time a component mounts. The value passed into useState is then set into React's internal state. For example, given
const Component = () => {
const [state1, setState1] = useState('one');
const [state2, setState2] = useState('two');
return 'foo';
};
React will then have the following state stored internally:
['one', 'two']
with one item in the (hidden, internal) state array for each call of useState that there is.
When a state setter is called, the corresponding item in the internal state array will be updated, and then the component will re-render. When a component re-renders, each useState call then returns the appropriate item in the internal state array. The first useState call will return the first item in the internal state array, the second useState call will return the second item, and so on.
In your code, with hooks, the click handler has a closure over the value of count that existed at that particular render of Counter.
if you were using class component you would have to use setState() in callback mode to access the previous state
Not really - if this code was a class component, it would be free to reference this.state.count directly without using the callback version.
I did a lot of research about Impure component vs Pure component, I am so confused about it and their behavior with Class & Functional components. So, please read carefully.
Question #1:
Is the definition of pure component is that "Pure component doesn't re-render when the value of changed state is the same value as the previous one", right?
Question #2 I can see that Functional Component behaves as pure component by default, because it doesn't re-renders the component if incoming state value is same as current one, am i right?.
const Home = () => {
const [count, setCount] = useState(1)
const handleCount = () => {
setCount(1)
}
console.log(`HOME ${count} - Functional Component`);
return (
<div>
<button onClick={handleCount}>Click Me</button>
<div>{count + 1}</div>
</div>
)
}
export default Home
See, It doesn't re-render when I click on button. Then it means functional components are pure components by default, right?
Question #3: Using functional component, when I set the default state to 0 and then call the setState like: setCount(1) then it should re-render only on first click because state has changed. But, when I click on 2nd time on button, it again re-renders, even current state value (1) is equal to incoming state (1), then why it re-renders on same value in functional component?
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(1)
}
I am so much confused with different definitions and different behaviors again and again.
Can someone help me with this?
Question #4: When component re-renders? On setState function call or when state value updates?
Question #5: If my default state value is true, and i call the update function like: setState(true). Will it still update the state value or it will not update the state value because it's same?
Lately i have been reading about hooks and was curious to know how they are internally implemented.
import React, { useState } from 'react';
function CounterUsingHooks() {
const [count, setCounter] = useState(5);
return (
<div>
<button onClick={() => setCounter(count + 1)}>
{count}
</button>
</div>
);
}
I understand that UseState accepts initial value and returns a pair which upon array destruction gets set as count and setCounter, where count is set to the first value returned by useState, and setCounter is the second.
I'm just interested in binding the function to the variable part.
why is it required to assign to UseState ? taking above example i can just write as below :
var count;
var setCounter=function(count){
count= count+1;
}
Could someone please explain me how exactly is UseState implemented ?
First, you need to understand the Fundamentals of State in React.
State is a JavaScript object that stores a component’s dynamic data and determines the component’s behaviour. Because state is dynamic, it enables a component to keep track of changing information in between renders and for it to be dynamic and interactive.
import React, { useState } from "react";
function HookCounter() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count {count}</button>
</div>
);
}
export default HookCounter;
React calls this function every time when it needs to re-render.
But why doesn't it initialize the state every time?
When exit the function, life of variables is ended, isn't it?
But how does it keep saving values of states?
I don't understand.
In useState function, is there any logic for that?
useState as a function is storing the value you gave it within React's core. When the component re-renders, React is going to pass the updated count from its core to the component.
More information here.
State is initialized only once when you create the component, this is how React works. From the documentation:
What does calling useState do? It declares a “state variable”. Normally, variables “disappear” when the function exits but state variables are preserved by React.
Just to have the context here, let me summarize what is useState and how it works in more details.
What is useState:
So useState is a hook which helps you to handle state in a functional component.
How is it working?
Once you call useState you need to pass the initial value of the state what you would like to use in your functional component. It returns a pair of values the count and setCount.
So let's have your example below:
const [count, setCount] = useState(0);
So useState returned two items where count is the current value. And the second item, setCount is a function which can be used to update the value of the count state.
count can be used to represent the state for example the value in a div element:
return (
<div>{count}</div>
)
In order to update the value you can achieve by calling setState(12).
From the docs you can read further here.
According to the React official website:
React keeps track of the currently rendering component. Thanks to the Rules of Hooks, we know that Hooks are only called from React components (or custom Hooks — which are also only called from React components).
There is an internal list of “memory cells” associated with each component. They’re just JavaScript objects where we can put some data. When you call a Hook like useState(), it reads the current cell (or initializes it during the first render), and then moves the pointer to the next one. This is how multiple useState() calls each get independent local state.
Reference:
How does React associate Hook calls with components?
Why doesn't useState function initialize State every time?
I think you're confused with:
const [count, setCount] = useState(0);
As it's just a variable!?!
Yes, it's just a variable but it will take everytime the function runs from the useState hook. And it is the local state like you have seen in class based component.