I'm using React hooks now. I've seen useState(null)[1] but I forgot where I seen it.
I wonder what's different from useState(null)?
In the docs, it says
Returns a stateful value, and a function to update it.
But what they mean is
Returns an array where the first position is a stateful value, and the second position is a function to update it.
The useState hook returns an array where the first position (index 0) is the state and the second position (index 1) is the setter for that state.
So when using useState(null)[1] you are only getting the setter for that state.
When you do
const [state, setState] = useState(null)
What you are doing is called Destructuring Assignment
And because in most cases you want to have both state and setState, destructuring makes it much easier to use than doing.
const hook = useState(null)
const state = hook[0]
const setState = hook[1]
With destructuring, you can make that with only one line which is much cleaner
And if you only want the setter, you can do it by
const setState = useState(null)[1] // only getting the setter
Just keep in mind that both are the same thing.
I wonder what's different from useState(null)?
useState(null) returns an array ([state, setState])
useState(null)[1] is accessing the returned array (setState)
The next expressions are equivalent:
const [, setState] = useState(null); // Destructuring assignment
const setState = useState(null)[1]; // Array index excess.
As useState returns an array of values, you can unpack values from the array.
Also, you can access (index into) an array item.
useState API.
Related
Hello everyone i have a qeustion about good practices, it is fine to initialize state with map in condition like this?:
const [items, setItems] = useState(
someArray ? someArray .map(item => ({ ...item, isActive: false })) : [],
);
Yes it's ok to do, but there is an improvement that you should add. At the moment, your mapping operation is going to occur on each rerender of your function if someArray is truthy. useState() will ignore anything its passed after the first initial render, so those additional .map() operations are wasted. To avoid doing those, you can pass a state initializer function into useState() which only runs on the initial render, avoiding all the unnecessary calls to .map() for any future rerenders of your component:
const [items, setItems] = useState(() =>
someArray ? someArray.map(item => ({ ...item, isActive: false })) : [],
);
You should also be aware that useState() only uses the value it's passed on the first render, so if someArray changes in subsequent rerenders, your items will not change as it will still hold the array it was initially passed when your component was first initialised. To change items, you would need to call setItems() instead. Alternatively, you may not need to use state at all if items needs to update on each render as someArray change and instead create a regular variable that you assign the result of calling .map(), which you can make more efficient by using useMemo().
Lastly, I would also consider using Array.isArray() as your ternary condition, as not all truthy values have a .map() method.
I'm using useRef to keep reference of a prop over renders during the lifetime of a component but I cannot seem to get past this type error:
Type 'MutableRefObject<any>' must have a 'symbol.iterator()' method that returns an iterator.
My code is:
const [values, setValues] = useRef<any[]>([])
Initially I was using an interface in place of any but changed to any while troubleshooting but still not working.
I have the empty array as the intial value. Not sure what I need to do here, please advise.
useRef does not work like useState. It doesn't return a value and an update callback. It just returns a ref object (type MutableRefObject).
const valuesRef = useRef<any[]>([]);
When you write const [values, setValues] = useRef<any[]>([]), you are attempting to destructure the returned value from the hook into two variables value and setValue. But you cannot destructure it because it is not an array (or an iterable). Thus you get the error:
Type 'MutableRefObject' must have a 'symbol.iterator()' method that returns an iterator.
You access the value of the ref through the .current property:
const values = valuesRef.current; // is type any[]
You update the values by setting the .current property:
valuesRef.current = [1, 2, 3];
Typescript Playground Link
React Docs: useRef
what will happen if usestate does not initialised with any value?
Eg: const [growth, setGrowth] = useState();
The implicit value is then (as it is for any elided function parameter) undefined.
I am currently learning React and React hook. A classic example of using useState is as below:
const [count, setCount] = useState(0);
My question is why the returned array is const? I think at least the value of count is changed over time.
The value returned by useState is not a const array, rather its just an array which the user has decided to declare as const. Think of the above as
const stateValue = useState(0);
const count = stateValue[0];
const setCount = stateValue[1];
So in short, the syntax const [count, setCount] = useState(0); is an Array destructuring syntax.
Not its is declared as const because you are not reassigning count or setCount to something else in your code, instead just using setCount method to update the state count.
React authors decided to return an array with state value and state setter so that you can name it anything you want instead of using a pre-decided name while destructuring.
In React State Hooks, one can write the following line to set a state variable called count and the setCount function to set the value afterwards, like below:
const [count, setCount] = useState(0);
Which is going to be the equivalent of writing:
this.state = { count: 0 };
My question is, how does the useState() function can get the name of the state variable -- count in this case, from the ES6 Destructuring Assignment statement?
Isn't the destructuing happens after the function has returned its value? Or is it possible to dynamically get the values that are being destructed, inside the function when it is invoked?
Update
Please note that I do understand that I can deconstruct to any name that I want, but how does the useState() knows what variable should go in the state, so it can be used later.
For example if I set two state variables, how does it distinguish between the two values, if the useState() function is not aware of the variable names?
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
When you use functional-components and useState
const [myCountVariable, setCount] = useState(0);
You only access your data using the myCountVariable variable. this.state isn't used.
If I understand correctly what you didn't understand is how it knows to write into 'this.state.myCountVariable' - it doesn't. The state doesn't store with the actual variable name.
Like the posts above me said, the useState assumes each time the component calls it it will call it in the same order so it returns "variable holders" based on index.
In React docs you can see they reference this in React Hook Rules:
Only Call Hooks at the Top Level
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls. (If you’re curious, we’ll explain this in depth below.)
Basically,
const [count, setCount] = useState(0);
is more accurately represented as
componentStateContainer[currentStateIndex] = myStateVariable; //React doesn't know how you named your local variable
currentStateIndex++;
return [myStateVariable, setStateMethod]
(currentStateIndex will reset to 0 when the function component is re-created)
It's returning an array that you destruct. The first index of the array is the value, the second the function. With array destructuring you can set a name for those variables
Example:
const [one, two] = ["test", () => {console.log(1)}];
console.log(one) // would be test
two() // would print out 1
More here:
https://medium.freecodecamp.org/array-destructuring-in-es6-30e398f21d10
useState returns an array, where first element is the value and second is the setter and using de-structuring you can give any name to it
For instance the above code is equivalent to
const state = useState(0);
const count = state[0];
const setCount = state[1];
Destructuring of arrays in JS is done by index, not by property name. Only the destructuring of objects is by property name.