i have a function, in which i am using a useState hook on a variable named active. This variable takes a string value, and on click it takes the value of the category which is clicked on. My goal is to use this value of category outside the function in order to perform operations on that particular category.
Is there a way in which i can export the value which this active useState variable has, outside this function anywhere in the project.
the structure of my code is as follows:
export const funct = () => {
// useState variable that will take the value of category that it gets clicked on/
const [active, setActive] = useState('');
return (
<div className='abc'>
{
content.map((info) =>
// Card is the category card which has a string value associated with, i.e. (info.name)
<Card key={info.name} cardInfo={info} state={active} onClick={() => setActiveState(info.name)} />)
}
</div>
)
}
any help would be appreciated. thank you
Depending on what you mean by "outside" the function there are generally 4 solutions.
put the state higher in your application scope, and pass the getter/setter into the component.
use React Context, wrap all components that need the shared state
use a 3rd party library like redux or zustand
roll your own "state outside of react" solution. i.e. Just stick a variable in global scope if you're not too concerned with concurrency, or use something like react-hooks-global-state or tips from this article or similar.
Related
This might be a really stupid question but I am writing my first project in React and am struggling to understand the purpose of setState hooks.
As far as I understand, the setState hook is used to set current values used in a component that is scoped to that component only, and does not persist if a page is reloaded for example, the value is simply held in memory until it is destroyed.
My question is, what is the difference between using setState() to store values and just simply declaring a let variable and updating it the regular way? Both methods just seem to be holding a non-persisting value scoped to that component. What is the difference?
changes in the state automatically cause your app to re-render (in most cases), so typically you store data in a state that is being displayed and possibly changed throughout the app (a menu whose options can change based on previous selections, for example).
TL;DR, even though the answer's not very long:
setState or useState is the key for React to subscribe to your component's state and update your components when necessary. Using let variables for storing app state means React will never get to know about state change and won't rerender and update your components.
A short overview of React
The core principle of React is that your components, and consequentially your UI, are a function of your app's state. When your app's state changes, components "react" to this state change and get updated. Here's a simple example:
const CounterButton = () => {
// Create a state variable for counting number of clicks
const [count, setCount] = React.useState(0);
// Decide what the component looks like, as a function of this state
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
};
ReactDOM.render(<CounterButton />, document.querySelector('#root'));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
This is a component that just creates a button that shows how many times it has been clicked. Now, the component needs to store information about how many times it has been clicked - this means that the clicks count is a part of this component's "state". That's what we get from React.useState(0) - a state variable whose initial value is 0, and a function that allows us to change the value. Whenever you call setCount with some value, React gets to know that the CounterButton component's state has changed and thus the CounterButton component needs a rerender.
So in other words, React allows you to neatly and concisely define what internal state a component requires and what the component looks like as a function of this internal state (and external props). React does rest of the work - it manages the app's state, and whenever a piece of state changes anywhere in the app, React updates the components that depend on that. In other words, components "react" to data change.
To your question
If you use a simple let variable instead of React.useState in the above example, React will no longer get to know if the count variable has changed. useState is the key for React to "subscribe" to your component's state.
const CounterButton = () => {
// React will never get to know about this variable
let count = 0;
return (
<button onClick={() => count++}>
Count: {count}
</button>
);
};
In fact, for a functional component, let variables won't work in any case because while rendering a functional component, React internally runs the function. That would mean your let variable would be reset to its default value. The above reason is more relevant to class components. Using let variables to store component state is like hiding things from React - and that's not good because then there's no one else to rerender your component when component state changes :)
This part of the React docs is a bit relevant - it does not go into any details, though.
React re-renders the new / updated state on an event occurance storing value into a variable wont trigger re-render and data is passed on form parent component to child component through props and a change in state can be reflected among all the parts.
For example we need to print 100 elements on a page if an element is modified or updated in any way this triggers re-render but using var if the variable is modified or updated this won't cause re-render where in data wont be updated.
What is wrong with the useCallback below that I do not get the values below every time the function onRefresh is called ?
How can I make it return the expected values using Hooks?
Example when I call onRefresh 2x
values expected:
true
0
20
false
true
0
20
false
values: received
false
0
0
false
false
20
20
false
initialization of the state variables
const [refreshing, setRefreshing] = useState(false)
const [offsetQuestions, setOffsetQuestions] = useState(0)
Function call with useCallback:
const fetchSomeData = async () => {
await new Promise(resolve => setTimeout(resolve, 3000)) // 3 sec
}
const onRefresh = useCallback( async () => {
setRefreshing(true)
setOffsetQuestions(0)
console.log(refreshing)
console.log(offsetQuestions)
await fetchSomeData()
setOffsetQuestions(20)
setRefreshing(false)
console.log(offsetQuestions)
console.log(refreshing)
}, [refreshing, offsetQuestions])
Where the function is called:
<FlatList
data={questionsLocal}
refreshing={refreshing}
onRefresh={onRefresh}
...
/>
What you are getting is the expected behaviour in hooks. It's all about closures. Each render in react has its own props, state, functions and event handlers and they forever stay the same for that particular render. So whats happening here is that the useCallback is closing over the state variables for that particular render, So the console.log even after a setState will always give you the value of the state for that particular render because of closure.
This situation confuses many developers who start using hooks. You would expect that React will be reactive and will change the value of the state after you called the setX, but if you think about it, you can not expect React to stop the execution flow change the state variable and continue execution.
So, what you are getting is the state variable from the scope of the creation of the useCallback (Read it again and again).
Let's break it down to two parts, the scope, and useCallback, as these are two different things you must understand to be able to use useState and useCallback.
Scope:
Lets say that you have a state called age and a function that write/reads this state (For now, without using useCallback)
const [age, setAge] = useState(42);
const increaseAge = () => {
setAge(age + 1); // this "age" is the same "age" from above
console.log(age); // also this one
}
From this you can see that every render a new age variable is defined (The value if given by React), also a new increaseAge function is defined with access to the scope that holds the age variable.
This is how the scope works, this is why you don't get a new value of age just after calling setAge. To be able to get a new value on age React will have to re-render the component, so a new age variable will be defined on a new scope. And this is exactly what happens after calling setAge, React will trigger a new render.
useCallback
Now, let's say that you don't want to define the increaseAge function again and again, you want to keep the same reference (Can help preventing useless renders). This is where you'd want to use useCallback.
When using useCallback, React provides the same function reference each render, unless one of the dependencies changes.
Without giving the useCallback the dependencies, React will not recreate the function and the function will access to the scope that it was created on (First render), when giving the useCallback the correct dependencies, it will be recreated, and now have access to the scope with the latest state variable.
This should explain why you must provide dependencies, and how state variables are accessed (scope).
You can use UseRef hook to use the current value of state instead of lexical value.
const ref = useRef();
useEffect(() => {
ref.current = value;
});
This how you can set value in a reference and use it wherever you want.
Below is the link where you can understand this problem in detail and with possible solutions
https://dmitripavlutin.com/react-hooks-stale-closures/
Thanks
Let's say there is a UI-independent state called currentSelected in React functional component. It stores the currently selected item and will be used at some time.
There are two ways to store the state, useRef hook or module scope out of component.
useRef hook:
function Example() {
const currentSelected = useRef()
useEffect(() => {
// access currentSelected state
})
function handleClick(item) {
currentSelected.current = item
}
return (
<ul>
{items.map(item => <li onClick={() => handleClick(item)}>item.name</li>)}
</ul>
)
}
module scope:
let currentSelected = null
function Example() {
useEffect(() => {
// access currentSelected state
})
function handleClick(item) {
currentSelected = item
}
return (
<ul>
{items.map(item => <li onClick={() => handleClick(item)}>item.name</li>)}
</ul>
)
}
Which method is more suitable for storing UI-independent state like currentSelected?
And what is the application scenario of useRef and module scope in storing state?
========= Update ===========
UI-independence means that you don't want to trigger re-render after updating the state. In contrast, UI related states do this.
The difference between useRef and a module-scope variable
Just for completeness sake, I'll throw in useState as well.
useState: immutable data that's tied to the component instance and triggers render on change through the setter function.
useRef: mutable data that's also tied to the component instance, but doesn't trigger any renders on change.
A module scope variable: mutable data that's tied to the module, which also doesn't trigger any renders on change since it's completely outside of React.
Use-case for useRef
If you're mounting a component more than once, e.g. using it on two pages, useRef will ensure that each component instance has its own mutable value.
// Here, the Example component could be used in multiple places
// and each instance would successfully keep its own state while
// not triggering renders on changes.
function Example() {
const currentSelected = useRef()
useEffect(() => { /* access currentSelected state */ })
return (
<ul>
{items.map(item => (
<li onClick={() => { currentSelected.current = item }}>{item.name}</li>
))}
</ul>
)
}
Use-case for a module scope variable
If you're looking for a singleton-like pattern or a static-like variable, e.g. for some kind of app-wide shared data, then a module scope variable will make this possible, like in any vanilla JS module.
// Here, the count value will be initialized once and then shared between every
// instances across the app, ever.
let count = 0;
function Example() {
// Won't trigger a render, so some Example instance could still show the old value.
count += 1;
return <span>Combined renders of all the Example components: {count}</span>;
}
Note that it won't trigger a render when count changes, so you shouldn't use it like that.
The caveats
If used only once in a place where the component is also only mounted once, both patterns will appear to behave similarly, but in the end, it's a matter of time until something triggers a remount and then you'll be facing a weird bug.
You could also encounter problems when unit testing a module with a module scope variable in it since it may not be properly reset in-between test cases. A quick workaround is to just export the variable and let the test cases change its value, but be careful not to change it anywhere else. Though this should be evaluated as a case-by-case basis.
First one
For myself choose the first one.Because its works as individual inside function.You could use multiple example component as same class/function .
function check(a){
let one = 'overwrite'+a;
console.log(one)
}
check(1);
check(2);//as individual inside only
Second one
Its overwrite currentSelected variable on each example component execute
let one = null
//second one
function check() {
one = 'overwrite';
console.log(one)
}
console.log('Before =' + one)
check();
console.log('After =' + one);
why not use the useState hook. it's perfect for this secnario.
const [currentItem, setCurrentItem] = useState();
...
{items.map(item => <li onClick={() => setCurrentItem(item)}>item.name</li>)}
another problem with your example is that when you change the ref current property in the handleClick it doesnt triggered render, so the useEffect in your function will not run so you cant access the ref.
Your "currentItem" may be UI-independent, but it would be component-dependent. right? If currentItem is related to this component, you should use the React.useState hook. This is just what the state is for.
Global variables(not constants) should be avoided in OOP programming. For example, there would be only one global variable in well-written OOP code, i.e. theApp.
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.
This question already has an answer here:
How does React implement hooks so that they rely on call order
(1 answer)
Closed 3 years ago.
I am currently working on my understanding of the useState hook from react.
What I would like to know is; when useState is called how is it able to correctly retrieve the state object and the function that can be used to modify it, for that specific functional component (assuming it has already been created the first time it was called.)
Another way to phrase my question would be where does count and setCount exist? useState() will obviously return a different state object and modifier function depending on which functional component useState is called in, so how does that work?
My guess would be that a closure is formed which means that this functional component has a lexical environment which consists of any local variables that were in-scope at the time the closure was created and this is what makes count and setCount available when useState is called. But I haven't been able to confirm this and since react is involved I could be way off.
export const MyFunctionalComponent = () => {
const [count, setCount] = useState(0);
return (
<h1>This is my component.</h1>
);
}
Can anyone clear this up for me?
#edit: I'm pretty sure I'm way off with my thinking about closures, looking at the react library source code I found the implementation for the useState function.
export function useState<S>(initialState: (() => S) | S) {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
I'm probably going to have to dig in and unpack this to get an answer.
I found the following on the React documentation page which at least gives a basic description on how useState is able to get back local state for functional components.
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.