Initial useState - map array inside useState - javascript

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.

Related

How To Fix Type MutableRefObject Must Have A Symbol.Iterator Method Error

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 is `useState(null)[1]` in React hooks?

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.

How does React State Hook gets the state variable name from the ES6 Destructuring Assignment

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.

Update state using function .map() is mutated state?

I'm still new to react and I still don't really understand about mutated the state.
I have seen many posts about this but I do not understand how the mutation process happened so I think it is necessary to ask this.
First i need to know is this called mutated state?
this.setState(prevState=>({
colors:this.state.colors.map((c,i)=>{
return{
original_color:c.original_color,
hex_color:c.hex_color,
isActive:false
}
})
}))
OR
let newData = this.state.colors.map((c,i)=>{
return{
original_color:c.original_color,
hex_color:c.hex_color,
isActive:false
}
})
this.setState({
colors: newData
})
in this case i just want to set all of this value isActive to false
Last
i want to set this value to empty
this.setState({
colors:[]
})
Since .map() returns a new array as a result, using it is safe and is not considered a mutation.
Basically, anything that doesn't change the original state or any direct references to it, is not considered a mutation.
Your state is not mutated in any case. .map() returns a new array. Your state is only mutated when you directly assign it to another value without calling .setState() like so:
this.state.value = anotherValue;
Or:
this.state.value.push(anotherValue)

Why my react state became not-immutable?

As I remember in past I could not update react-state manualy.
Now, I don't know why, I can do it.
My case:
and the property is changed here!It works! Why?
P.s: I use react16.02
the state does mutate in react. and u should be very careful that u don't override it like that or you will encounter unexpected result. it is always best to copy your state into another object, then change its properties as u like and after you are done use set state to set set your object state as copied object.
you can use Object.Assign or spread operator for this. spread operator is easier when you are using Arrays.
this.state = {
myObj : {propA: "A" , propB: "B"};
}
then at some point you want to change it
let myObj = this.state.myObj;
let copyMyObj = {...myObj};
copyMyObj.propA = "C";
this.setState({
myObj: copyMyObj
});
In javascript objects are mutable.
For example if
var a = {"something": 1}
and if you assign it to another variable b and if you change its value, the value of object a is also changed
var b = a;
b.something = 2;
console.log(b);
console.log(a);
//output {"something": 2}
both will print {"something": 2}. The value in a is also changed.
Similarly in your code you are directly assigning the state value in line number 61 and mutating it in line number 62.
So better way is to use
let newOptions = {...this.state.newOptions };
or
let newOptions = Object.assign({},this.state.newOptions}
This will create new object.
React state is mutable, so you can update react state directly. But it is recommended not to mutate react state due to multiple reasons. One of the reasons is, if you do this.state.count = 3, react DOM will behave in an unpredictable manner such as overwriting data and not re-rendering. Performance tuning is another reason.
React on calling setState() will call the render method in some near future which will cause the DOM to be updated with new values.

Categories

Resources