There is an array of inputs in two forms: 1. Empty when created. 2. With the created values from the server after creation and saving. My code for adding a value for each of the inputs in the array doesn't work in the second case and I don't understand what could be wrong. In the first case, the values are written normally, and in the second, nothing happens.
<input
defaultValue={sectionValue[i].text}
value={sectionValue[i].text}
onChange={(e: React.ChangeEvent<HTMLInputElement>): void => {
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
}}
/>
There are two issues:
Any time you're changing state based on existing state, you're best off using the callback form of your state setter, because it's really easy for the handler to close over a stale copy of the state (sectionValue in your case).
You're breaking one of the rules of state: don't directly modify state. You're directly modifying the sectionValue[i] object. So it's the same object, but with a different text property. Later you're copying the array, but you need to copy the object as well. Sometimes it'll happen to render correctly when you do this, but other times — often — it won't.
To fix both, change:
sectionValue[i].text = e.target.value;
setSectionValue([...sectionValue]);
to
setSectionValue(oldSectionValue => {
const newSectionValue = [...oldSectionValue];
newSectionValue[i] = {
...oldSectionValue[i],
text: e.target.value
};
return newSectionValue;
});
There's more than one way to do that, but the basic things are: 1. Use the callback, and 2. Copy both the object and the array.
Side note: Since sectionValue is an array, I'd suggest using the plural for it (sectionValues).
Related
I'm using React hooks to implement a form. The state of the form is being managed as an array of objects. A feature/bug of React appears to be that copies of arrays are processed/unified/mutated as a single array. That is, changes to one array become changes in all copies of the array.
I do not want to mutate the array as it creates an issue when attempting to use an array to initialize and reset the state. Once the initialization array becomes unified with the state array, the reset function will no longer work. I have made some attempts at preventing array unification without success. Below is the problem, some of my attempts to resolve the issue and a work around.
Lets say the state of the form is defined as:
const [field, setField] = useState(initialField);
Once this code executes, the value of array initialField is unified with the value of the array field.
Lets say we want to reset the form and use a function like this:
const reset = () =>{setField(initialField)}
This reset function will not work. Once the value of initialField is unified to the current value of field, setField will always contain the current value of field.
Lets say we replace initialField with:
initialField.map((e)=>{return e;})
The map method is immutable. It should create a separate array. However, replacing initialField with a map method of initialField does not change the result. useState still unifies the value of initalField with field.
Using the separator operator, {...intialField}, does not change the outcome. Neither does using intialField.concat(). Lets say we assign initalField to another array resetField. React will unify together all three arrays: field, initialField and resetField.
Lets say we hard code the array into the setState function. Assuming we have an array of N objects, it would look something like:
const [field, setField] = useState([{object 0}, {object 1} ... {object N}] );
The reset function is still:
const reset = () =>{setField(initialField)}
The reset function will work exactly once. Once the code is executed, React unifies initialField with field, so the reset function will no longer work. Lets say we replace initalField with a map method, a separator operator or a concat method. React will still unify the field array with initialField.
A work around this is to hard code the array into the setState and the reset functions as follows:
const [field, setField] = useState([{Object 0},{Object 1},...{Object N}]);
const reset = () =>{setField([{Object 0},{Object 1},...{Object N}])};
This is an ugly solution that complicates maintenance as the same changes have to be made twice.
Practicing on developing To-do list using React.
Now I'am at the point when have to arrange a deleting tasks, one by one.
This is how I achieved it:
handleDeleteTask(id) {
const remainingTasks = this.state.tasks.filter(task => id !== task.id);
this.setState({ tasks: remainingTasks });
localStorage.clear();
localStorage.setItem('storageTasks', JSON.stringify(remainingTasks));
}
Basicaly I have got remaining tasks filtering tasks from state, and updating state.
To update tasks in Local storage I managed to clear all and insert a new Updated Array.
Will it be more efficient to use the map() and slice() method to eliminate just needed object and not replacing an Array?
Will it be more efficient to use map() method and slice() to eliminate just needed object and not replacing an Array?
It doesn't matter, because you aren't allowed to modify the array in state directly anyway. (Even if you could, it really wouldn't matter unless the array is huge, and it would be hard for us to say whether to would be better or worse without seeing the code. It isn't immediately clear why map would be involved, for instance.) So your filter approach is mostly correct.
There is a problem with it, though: Whenever you're updating state based on existing state, you have to use the callback form of setState, not the one you pass a new value to directly:
handleDeleteTask(id) {
this.setState(({tasks}) => {
tasks = tasks.filter(task => id !== task.id);
// localStorage.clear(); // You probably don't need or want this
localStorage.setItem("storageTasks", JSON.stringify(remainingTasks));
return {tasks};
});
}
I have the following selector:
const getAllAddresses = (withStartEnd) =>
createSelector(
[getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
(all, startAddress, endAddress) => {
if (!withStartEnd) return [...Object.values(all)];
return [startAddress, ...Object.values(all), endAddress];
}
);
I noticed that the selector is re-calculating every time, event when all, startAddress and endAddress do not change. If I remove the input for the selector function, to something like this:
const getAllAddresses = (
createSelector(
[getAllAddressesSelector, getStartAddressSelector, getEndAddressSelector],
(all, startAddress, endAddress) => {
return [startAddress, ...Object.values(all), endAddress];
}
)
);
Then everything works as expected and the selector does not re-calculate on every call. Seems like I missing something in the selector concept. Any help would be much appreciated.
Update:
Please refer to How do I create a selector that takes an argument?
In short: the way you did it, will work only if you pass static arguments and create a factory function outside of mapStateToProps. For dynamic arguments it's more complex and please follow the resource I already mentioned above.
The reason your selector is recalculated each time mapStateToProps is called is that calling getAllAddresses will create a new instance of createSelector and the memoization won't work.
Original answer:
In short, reselect determines input selector changes, based on identity check ===.
Therefore, if your input selectors always create and return a new object or array, then your selector will be recalculated each time.
In order to fix the recalculation issues:
Make sure your input selectors always return a reference, instead of new object / array.
Or, if a new object / array is the proper returned value, then you have to customize the equalityCheck for defaultMemoize
From the reselect docs:
Why is my selector recomputing when the input state stays the same? (please follow the link, there are great examples)
Check that your memoization function is compatible with your state update function (i.e. the reducer if you are using Redux). For example, a selector created with createSelector that recomputes unexpectedly may be receiving a new object on each update whether the values it contains have changed or not. createSelector uses an identity check (===) to detect that an input has changed, so returning a new object on each update means that the selector will recompute on each update.
this is my problem.
when I use the push() function it changes my props in react.
const prioship = (this.props.orders.prioshipping === false ? {'name':`Regular Shipping`,'price':'0','currency':'EUR','quantity':1, 'description': ''} : {'name':`Priority Shipping`,'price': this.props.prices.shipping['A'].toString() ,'currency':'EUR','quantity':1, 'description': ''})
console.log('#### TOKEN ORDER #####1', this.props.orders.warenkorb)
const orders = this.props.orders.warenkorb
const order2 = this.props.orders.warenkorb
orders.push(prioship)
console.log('#### TOKEN ORDER #####2',order2, this.props.orders.warenkorb)
So even at the level of the console log 'TOKEN ORDER 1' this props have the "prioship" in it even though it happens later in the code. I don't understand how to make it stop this. I just want a variable 'orders' where the prioship is in it, i dont want my props to change.
Please help
Never mutate props, which you're doing here.
Make a new array instead, and don't modify the original array.
const orders = this.props.orders.warenkorb.concat( prioship );
Array.push() "mutates" (changes/modifies) the original array. Array.concat() returns a new array, and does not modify the original.
As Andy Ray mentioned, don't change the props directly.
The right way is to use const orders = this.props.orders.warenkorb.slice(), which will give you a copy of the array in the props and allow you to use this array later without changing the original props.
Lastly, the reason your 1st console.log('#### TOKEN ORDER #####1', this.props.orders.warenkorb) is showing you the later value is because the console will show the values by reference. If you want the exact value at where you're printing you can use: console.log('#### TOKEN ORDER #####1', JSON.stringify(this.props.orders.warenkorb));
Two things are happening here.
First of all you are modifying a prop in an object. even if you save the property in another variable all you are doing is saving a reference to that property. If you want to avoid that you can use concat as Andy Ray pointed out.
Second thing. You see the change even at TOKEN ORDER 1 because console.log can be a bit tricky. Since objects store references to their properties, if you change the object later it will show you the latest update. More on this here.
I am looking for a best practice to have a ReactJS component responsible for the form for users to edit a given entity. Very simplified example here. Actual forms would in many cases have several more fields and more GUI functionality.
React.createClass({
getInitialState: function() {
return {
entity: {
property1: null,
property2: null
}
};
},
handleChange: function(e) {
var entity = this.state.entity;
switch(e.target.name) {
case 'property1':
entity.property1 = e.target.value;
break;
case 'property2':
entity.property2 = e.target.value;
break;
}
this.setState({
entity: entity
});
},
render: function() {
return (
<div className="entity-form">
<form onChange={this.handleChange}>
<input type="text" name="property1" value={this.state.entity.property1} />
<br />
<textarea name="property2" value={this.state.entity.property2}></textarea>
<br />
</form>
</div>
);
}
});
The fields of the form is directly editing an entity object, that could then be saved to a RESTful api. I want the component to be updated as the user change the fields, so the GUI could react based on the input during typing (like validations, info etc).
In theory, I could have the whole state object represent the entity that is being edited, so every property of the entity is first level state variables. However, I want to be able to add additional state variables for GUI functions and other things related to what the component is going to do, so I would prefer the entity object to be one state variable like the "entity" state variable above. The object could of course be some more complicated object, like a Backbone model or similar, but in this simplified example, I just use a simple object whit the required properties.
So, in search of the best practice way to make React components for this purpose, I have some questions:
Props or state.
In this case, I have chosen to put the entity object with the content for the form in a state variable instead of prop. This is to be able to update the object during form input without having to call the parent and update the props. As far as my React experience goes, that would be the best practice for a form component like this.
Controlled or uncontrolled inputs.
In the simplified example above, I use controlled inputs. This leads to updating the state and re-rendering the component on every change (like every character entered of a text field). Is this the best practice? The good thing is that the component has full control of what happens, instead of having defaultValue paramters, and on some event (like the user pressing a save button), the component extract the values, update the entity and save it to the server. Is there any reasons (or opinions) on if controlled or uncontrolled inputs should be used in cases like this?
onChange for the form or every input
The example has an onChange on the form tag, and it causes the handleChange method to be called every time any of the fields in the form is changed. However, since the inputs are controlled (have value parameters), React complains that the input fields does not have an onChange property. Does this mean having a common onChange on the form tag is bad practice, and I should remove it and put onChange on every single field instead?
Updating individual properties
In the above example, I use a switch based on what input field is being update (when handleChange is called). I guess I could instead make sure all field names is in sync with the property names of the entity, and I can set properties of the entity object in handleChange based on the name of the field from the event (e.target.name). However, this makes it hard to have individual needs per field, even if most fields just update an entity property directly. I guess an alternativ is a switch with a default block setting based on the name of the input, and case blocks for any field that require other ways of updating (like filtering the value before setting it on the entity). Please comment this if you know some much better way of handeling field updates this way.
Updating the state entity
One big problem of this example, is the way the entity object is updated. Since the entity variable in the handleChange is set to the entity object from current state, this is just a pointer, and updating the entity variable will change the object in state. The React pages say you should never update state directly. One of the reasons is something I have experienced when updating the state this way before calling setState. If having a shouldComponentUpdate method, the prevState contain the new state, since the content of the prevState argument sent to the shouldComponentUpdate is based on what was in the state when setState was called. As far as I know, there is no simple way to clone a object in javascript. So the question is, when having whole objects that I need to update properties of (and not touching the other values in the object) instead of just running setState of a single state variable, what is the best way to do this without causing theese kinds of state mixups?
Anything that is going to change goes in State.
If you're looking at loading an existing entity and editing it, you want controlled inputs, and you want to set the values accordingly. I tend to stay away from defaultValue in most cases (outside of dropdowns)
This ties back in to your previous question. If you specify a value, you are using a controlled input, and you have to provide an onChange handler for any controlled input, otherwise it is set in stone. A benefit of controlled inputs is that users can't edit them, so if you had some properties locked down (maybe for read only, security reasons), when the user attempts to save, even if they edited the HTML directly, React should pull the property's value from the vDOM representation (could be wrong here, but I believe I've tested this before). Anyway, you have to have onChange set directly on controlled inputs. As far as using event delgation (at the form level), this isn't a bad practice at all for a lot of events, this is just a specific scenario (controlled inputs) where you need onChange events specified for each element.
Not entirely sure what the ask on this one is, but I used refs instead of target.name.
So, you're correct in that you should never alter the state directly, and this is a tricky bit from the docs. React is going to alter state directly, it's just going to do it in the implementation through setState. If you alter state outside of this method call, unexpected things will happen and errors will be thrown.
shouldComponentUpdate only does shallow comparisons, but there are a few solutions here.
One is to stringify the objects, this is a quick and dirty object comparison, don't really recommend it, but it works.
A better solution, and one I have used with React + Flux is to implement a propertyChanged bool, and just check that in your shouldComponentUpdate.
Now, this will require you to be aware of setting it when things change, i.e., you changed something deeper in the object graph. Say propertyOne is an object with a property that gets changed in your handleChange method. You would validate the input however you wish, then set propertyChanged = true, and you then need to implement componentDidUpdate. We're making an assumption here, but if the component has updated, you set propertyChanged back to false so you don't have any further triggering of unwanted updates. I hope that makes sense. It's kinda like a one-way notifyPropertyChanged.
I'm providing a quick example of what I would probably do for a more dynamic implementation that allows you to add more properties to your object (only shallow properties in this implementation, obviously you could write a more robust solution). Let me know if you have any further questions or if I didn't answer something.
http://jsfiddle.net/rpv9trhh/
var e = {
prop1: 'test',
prop2: 'wee',
prop3: 'another property',
propFour: 'Oh yeah!'
};
var FormComp = React.createClass({
getInitialState: function(){
return {
entity: this.props.entity
}
},
render: function() {
var ent = this.state.entity;
var that = this;
var inputs = [];
for(var key in ent){
inputs.push(<input
key={key}
style={{display:'block'}}
type="text"
ref={key} onChange={that._propertyChanged.bind(null, key)}
value={ent[key]} />)
}
return <form>
{inputs}
<input
type="button"
onClick={this._saveChanges}
value="Save Changes" />
</form>;
},
_propertyChanged: function(propName) {
var nextProp = this.refs[propName].getDOMNode().value;
var nextEntity = this.state.entity;
nextEntity[propName] = nextProp;
this.setState({
entity: nextEntity
});
},
_saveChanges: function() {
var updatedEntity = this.state.entity;
for(var key in updatedEntity){
alert(updatedEntity[key]);
}
//TODO: Call to service to save the entity, i.e.
ActionCreators.saveEntity(updatedEntity);
}
});
React.renderComponent(<FormComp entity={e} />, document.body);