React.js updating state vs immutability - reactjs

Is there any option to add zone without maping it and adding new object into state on first click?
https://codesandbox.io/s/charming-ellis-b7e8k?file=/src/App.js:319-324
My wanted behaviour is filling inputs and adding it into state on click. After added I will reset input to add more zones into state.
Someone told about using immer.js. Actually reading docs.. is there any other way?
Thank you

useState will require objects to be immutable to detect changes. So, array.push will usually not have the desired effect. Create a new one instead:
onClick={() =>
setZones([...zones, {
Server: "",
positionName: "",
positionIdentification: ""
}])
}
This will create a new array with the contents of the old plus the new content.
To avoid creating a new array every time, as you suggested immer can do that for you with the useImmer hook.

Related

My React state does not update using useState hook with a slightly complexer object

In my React app I purposely combined several arrays in one state using a useState hook. These arrays represent visual objects that I would like to keep "managed" together to ensure that that re-renderings of my application are visually consistent.
While testing I tried to change a property of some objects of my first array. updatedElements reflects the update properly (clearly shown by my console-log). However, updating my useState state does not work. The array elements does not change at all.
Here is the relevant code:
const updatedElements: VisualDiagramElementData[] =
visualData.elements.map((element: VisualDiagramElementData) =>
element.id === id
? { ...element, selected: true }
: { ...element, selected: false }
);
console.log(updatedElements);
setVisualData({
elements: updatedElements,
connectors: visualData.connections,
connections: visualData.connections,
});
What am I missing / doing wrong? Any help is highly appreciated!
When updating a state property based on its previous value, the callback argument of the state setter should be used. Otherwise you may use stale state values.
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
https://reactjs.org/docs/hooks-reference.html#functional-updates
It should be:
setVisualData((visualData)=>({
elements: visualData.elements.map(/*Your map callback*/),
connectors: visualData.connections,
connections: visualData.connections,
}));
I would go with what is recommended in the React docs - split them out into multiple state variables:
https://reactjs.org/docs/hooks-faq.html#should-i-use-one-or-many-state-variables.
Depending on how you trigger the state updates will determine whether re-render occurs multiple times or not.
See this answer for more details on how the batching works:
https://stackoverflow.com/a/53048903/6854709
Also take note of the comment by aprillion:
Note from github.com/facebook/react/issues/10231#issuecomment-316644950 - This is implementation detail and may change in future versions.
Eventually, it worked by using the "callback argument of the state setter". I revised / reviewed my entire code and I modified all my state setters accordingly. Now, everything works like a charm. Thank you very much again for your help, especially to Nice Books.

React Native, node js - update object in different scripts

I'm building an app in React Native,
and I am trying to understand if I need to use useState & useContext to export a user info object,
or I can just get the object from the script, change it, and it will be changed to all the other scripts.
(or if there is a better way to do it so)
Thanks in advance :)
If you update data in your component, always use useState.
Furthermore if you store an array or an object into your state, it's a good practice to update the array/object, and then update your state with a spread operator (this will create a new array/object, and fired your useEffect and UI)
Example :
If you do a simple :
const [stateArray, setStateArray] = useState(["foo"])
stateArray[0] = "new foo"
All the components that consume the stateArray[0] will not update and continue to display a simple foo
This is caused becaused this update doesn't triggered a refresh as you're array isn't update, but one of it's component (not detect by React).
If you want an update, you need to do :
const [stateArray, setStateArray] = useState(["foo"])
stateArray[0] = "new foo"
setStateArray([...stateArray])
This will create a new array, and as such, triggered a new render of your component.
And the same logic applied for object, if you only update one of its value, there will be no update, you need to create a new object :
Example :
const [stateObject, setStateObject] = useState({foo: "foo"})
stateObject.foo = "new foo"
setStateObject({...stateObject})

How can I update an object in a nested array when an input value changes?

I have a list of articles, each with some number of exercises, and each exercise has a duration property. The duration property is editable, but I can't seem to figure out how to update this so it takes effect in my original array.
I've setup a basic example in CodeSandbox.
Expected behavior is when changing one of the input fields, it should update in my articles array (which is located in App.js)
I'm trying to do: onChange={(e) => setDuration(e.target.value)}
As I see it, the problem is that React doesn't like if you change the current state, so I would have to create a new state... So this "new" duration should somehow propagate to App.js maybe? Or is the solution to have a method in App.js that you pass down all components to update the original array?
Any advice would be greatly appreciated :)
changing the state in the app itself doesn't change the object the app received.
you'll need to manually change the object
try adding:
const onChange = (e)=>{
exercise.duration = e.target.value;
setDuration(e.target.value)
}
and change your
onChange={(e) => setDuration(e.target.value)}
to:
onChange={onChange}
NOTE: this wont cause the parent to rerender.

Is it possible to save components state when they are stored in an array manipulated?

I'm trying to create a stepper form
I store my steps in an array of json with a proprety component ({typeOfComponent, component, key})
It works wells, but:
Everytime i slice my array, like when i move up/down a step or add a new step between two steps.
I lose the states inside my component.
I tried to use memo, i don't understand why it's only when an item position my composent is recreate. Is it possible like a pointer in C to store only his "adress"
the code sandbox exemple =>
https://codesandbox.io/s/infallible-maxwell-zkwbm?file=/src/App.js
In my real projet, the button ADD is a button for chosing the new step type
Is there any solution for manipulates my steps without losing the user data inside ?
Thanks for your help
React is re-mounting the components inside of this every re-render probably due to a variety of reasons. I couldn't get it to work as is, but by lifting the state up from your components, it will work.
You'd likely need to lift the state up anyway because the data isn't where you need it to be to make any use of your form when the user is done with it.
In order to lift the state up, I added the current value to the steps array:
function addNext(step, index) {
componentKey++;
setSteps(prevState => {
let newState = [...prevState];
step = 1;
newState.splice(index + 1, 0, {
stepNumber: step,
component: getStepContent(step, componentKey),
value: getDefaultValue(step),
key: componentKey
});
return newState;
});
}
I also made sure your getStepContent just returned the component rather than a node so you can render it like this:
<step.component
value={step.value}
onChange={handleChange}
data-index={i}
/>
There are definitely a lot of ways to optimize this if you start running into performance issues, of course.
https://codesandbox.io/s/beautiful-river-2jltr?file=/src/App.js

Store checkbox values as array in React

I have created the following demo to help me describe my question: https://codesandbox.io/s/dazzling-https-6ztj2
I have a form where I submit information and store it in a database. On another page, I retrieve this data, and set the checked property for the checkbox accordingly. This part works, in the demo this is represented by the dataFromAPI variable.
Now, the problem is that when I'd like to update the checkboxes, I get all sorts of errors and I don't know how to solve this. The ultimate goal is that I modify the form (either uncheck a checked box or vice versa) and send it off to the database - essentially this is an UPDATE operation, but again that's not visible in the demo.
Any suggestions?
Also note that I have simplified the demo, in the real app I'm working on I have multiple form elements and multiple values in the state.
I recommend you to work with an array of all the id's or whatever you want it to be your list's keys and "map" on the array like here https://reactjs.org/docs/lists-and-keys.html.
It also helps you to control each checkbox element as an item.
Neither your add or delete will work as it is.
Array.push returns the length of the new array, not the new array.
Array.splice returns a new array of the deleted items. And it mutates the original which you shouldn't do. We'll use filter instead.
Change your state setter to this:
// Since we are using the updater form of setState now, we need to persist the event.
e.persist();
setQuestion(prev => ({
...prev,
[e.target.name]: prev.topics.includes(e.target.value)
// Return false to remove the part of the array we don't want anymore
? prev.topics.filter((value) => value != e.target.value)
// Create a new array instead of mutating state
: [...prev.topics, e.target.value]
}));
As regard your example in the codesandbox you can get the expected result using the following snippet
//the idea here is if it exists then remove it otherwise add it to the array.
const handleChange = e => {
let x = data.topics.includes(e.target.value) ? data.topics.filter(item => item !== e.target.value): [...data.topics, e.target.value]
setQuestion({topics:x})
};
So you can get the idea and implement it in your actual application.
I noticed the problem with your code was that you changed the nature of question stored in state which makes it difficult to get the attribute topics when next react re-renders Also you were directly mutating the state. its best to alway use functional array manipulating methods are free from side effects like map, filter and reduce where possible.

Resources