EDIT: this is a duplicate, see here
I can't find any examples of using a dynamic key name when setting the state. This is what I want to do:
inputChangeHandler : function (event) {
this.setState( { event.target.id : event.target.value } );
},
where event.target.id is used as the state key to be updated. Is this not possible in React?
Thanks to #Cory's hint, i used this:
inputChangeHandler : function (event) {
var stateObject = function() {
returnObj = {};
returnObj[this.target.id] = this.target.value;
return returnObj;
}.bind(event)();
this.setState( stateObject );
},
If using ES6 or the Babel transpiler to transform your JSX code, you can accomplish this with computed property names, too:
inputChangeHandler : function (event) {
this.setState({ [event.target.id]: event.target.value });
// alternatively using template strings for strings
// this.setState({ [`key${event.target.id}`]: event.target.value });
}
When you need to handle multiple controlled input elements, you can add a name attribute to each element and let the handler function choose what to do based on the value of event.target.name.
For example:
inputChangeHandler(event) {
this.setState({ [event.target.name]: event.target.value });
}
How I accomplished this...
inputChangeHandler: function(event) {
var key = event.target.id
var val = event.target.value
var obj = {}
obj[key] = val
this.setState(obj)
},
I just wanted to add, that you can also use de-structuring to refactor the code and make it look neater.
inputChangeHandler: function ({ target: { id, value }) {
this.setState({ [id]: value });
},
this.setState({ [`${event.target.id}`]: event.target.value}, () => {
console.log("State updated: ", JSON.stringify(this.state[event.target.id]));
});
Please mind the quote character.
I had a similar problem.
I wanted to set the state of where the 2nd level key was stored in a variable.
e.g. this.setState({permissions[perm.code]: e.target.checked})
However this isn't valid syntax.
I used the following code to achieve this:
this.setState({
permissions: {
...this.state.permissions,
[perm.code]: e.target.checked
}
});
In loop with .map work like this:
{
dataForm.map(({ id, placeholder, type }) => {
return <Input
value={this.state.type}
onChangeText={(text) => this.setState({ [type]: text })}
placeholder={placeholder}
key={id} />
})
}
Note the [] in type parameter.
Hope this helps :)
I was looking for a pretty and simple solution and I found this:
this.setState({ [`image${i}`]: image })
Hope this helps
With ES6+ you can just do [${variable}]
when the given element is a object:
handleNewObj = e => {
const data = e[Object.keys(e)[0]];
this.setState({
anykeyofyourstate: {
...this.state.anykeyofyourstate,
[Object.keys(e)[0]]: data
}
});
};
hope it helps someone
Your state with dictionary update some key without losing other value
state =
{
name:"mjpatel"
parsedFilter:
{
page:2,
perPage:4,
totalPages: 50,
}
}
Solution is below
let { parsedFilter } = this.state
this.setState({
parsedFilter: {
...this.state.parsedFilter,
page: 5
}
});
here update value for key "page" with value 5
try this one please.
State is here as example
this.state = {
name: '',
surname: '',
username: '',
email: '',
}
Onchange function is here.
onChangeData = (type, event) => {
const stateData = this.state;
if (event === "" || event === "Seçiniz")
stateData[type] = undefined
else
stateData[type] = event
this.setState({ stateData});
}
Can use a spread syntax, something like this:
inputChangeHandler : function (event) {
this.setState( {
...this.state,
[event.target.id]: event.target.value
} );
},
Related
I have an object in the state ,
this.state = {
selectedValue: {}
}
Now,Here I am adding a property to this by object in the following way
if (e.currentTarget.checked) {
this.setState({
selectedType: {
...this.state.selectedType,
[resumeId]: type
}
})
Now, In else part I have to remove the property with the matching resumeId.
Or Do I need to create an array of objects ? I am kind of confused here.
Can any one help me with this ?
The best way to do this is add a prefix to your resumId:
if (e.currentTarget.checked) {
this.setState({
selectedType: {
...this.state.selectedType,
[`resume-${resumeId}`]: type
}
})
Now, you have a way to identify your resumeId. Then loop through your selectedType state and remove resumeId. You can do it as the following:
let selectedType = this.state.selectedType;
for (let key in selectedType) {
if (key.indexOf('resume') !== -1) {
delete selectedType[key]
}
}
this.setState({selectedType})
if (e.currentTarget.checked) {
this.setState({
selectedType: {
...this.state.selectedType,
[resumeId]: type
}
}) else {
const selectedType = {
...this.state.selectedType
}
delete selectedType[resumeId];
this.setState({
selectedType
});
}
You can delete the resumeId from the object iself.
Use Object destructuring to acheive this cleanly:
if (e.currentTarget.checked) {
this.setState({
selectedType: {
...this.state.selectedType,
[resumeId]: type
}
})
} else {
// Destructure the resumeId and rest properties
const { resumeId, ...rest} = this.setState.selectedType;
// Only assign the rest properties now
this.setState({ selectedType: ...rest });
}
Update:
To check if all values are same:
const data = { "a": "11", "b": "11", "c":"12", "d" : "11" };
const objectValues = Object.values(data);
// Check first value with every value
const isEveryValueSame = objectValues.every(x => x === objectValues[0]);
console.log(isEveryValueSame);
I have this code in my constructor:
this.state = {
tests: [
{
question: "1",
answer: "2",
user: ""
},
{
question: "1",
answer: "2",
user: ""
},
],
};
I have edit function where I read event value in my input:
edit(id, event) {
this.state.tests[id].user = event.target.value;
this.setState({tests:this.state.tests});
}
But es give me this warning:
Do not mutate state directly. Use setState()
react/no-direct-mutation-state
What can i do in this case? Maybe somehow change the line with the assignment event.target.value into setState()?
You can use map() to create copy of tests
edit(id, event) {
const user = event.target.value;
const tests = this.state.tests.map((x,i) => i === id ? {...x, user} : x);
this.setState({tests});
}
One way I tend to go is to make a copy of the array first and then change an item in it, or change the array itself, and then set the state
var tests = this.state.tests.slice(0);
tests[id].user = event.target.value;
this.setState({tests:tests});
You may want to deep-clone the array in some cases, sometimes not.
You are correct, that the problem is with the line:
this.state.tests[id].user = event.target.value;
That's the point where you are mutating your state directly.
You have a few options.
You can "clone" the array first and then update it:
const newTests = [...this.state.tests];
newTests[id].user = event.target.value;
this.setState({tests: newTests});
You could also use immutability-helper:
const {value} = event.target;
this.setState(prevState => update(prevState, {[id]: {user: {$set: value}}}));
In this example, you need to extract value from your event, because it's not safe to access event values in asynchronous calls after an event has been handled.
edit(id, event) {
var newNote = {...this.state.tests[id]}
newNote.user = event.target.value
this.setState({ tests: [...this.state.tests, newNote]})
}
First of all, when you try to set a the new state using the data from previous state you have to use the updater as a function
https://reactjs.org/docs/react-component.html#setstate
const edit = (id, event) => {
this.setState((prevState) => {
const tests = [...prevState.tests];
tests[id] = {
...tests[id],
user: event.target.value
};
return {
...prevState,
tests
};
});
};
When state is not heavy, I use the following codes:
edit (id, event) {
const cp = JSON.parse(JSON.stringify(this.state.tests))
cp[id].user = event.target.value
this.setState({ tests: cp })
}
Update: I found Immer solves it perfectly.
import produce from 'immer'
edit (id, event) {
this.setState(
produce(draft => draft.tests[id].user = event.target.value)
)
}
I have a function to add item on bag. It's working pretty cool but I got a warning Do not mutate state directly. Use setState();
How can I use this.setState() instead use this.state.bagList[item.id] = {...item};
addToBag = (item) => {
let itemFound = false;
Object.keys(this.state.bagList).map((key) => {
const bagItem = this.state.bagList[key];
if(bagItem.id === item.id) {
itemFound = true;
bagItem.quantity++;
return bagItem;
} else {
return bagItem;
}
});
if(!itemFound) {
this.state.bagList[item.id] = {...item};
}
const newbagList = this.state.bagList;
this.setState({ bagList: newbagList });
localStorage.setItem("productsOnBag", JSON.stringify(newbagList));
this.showBag();
}
I expect prevent this warning and mutate the state correctly.
You can use setState in the following way to update bagList without the mutation.
this.setState((prevState) => ({
bagList: {
...prevState.bagList,
[item.id]: {...item}
}
}))
Hope this will help!
I tried some other approaches, but I solved the warnings just adding my state in a const and attributing the {...item}. Here is the code:
...
if(!itemFound) {
const addItemToBagList = this.state.bagList;
addItemToBagList[item.id] = {...item };
}
The problem is in the order of your code.
You are modifying state directly here:
if(!itemFound) {
this.state.bagList[item.id] = {...item};
}
That's why it's throwing an error.
You should first create newbagList, and after that modify it and this.setState({ bagList: newbagList });
I have json data like this:
[{"TICKER":"APPL","ASK":192.12345,"BID":193.54321,"ISCHANGED":"NO"},
{"TICKER":"TSLA","ASK":318.98765,"BID":319.56789,"ISCHANGED":"NO"}]
On component update with new data I would like to compare/map the previous and the new one data and to identify changes using "ISCHANGED":"YES". Ticker is the Key, Bid/Ask - changing values.
Thanks
UPDATED with the example code:
this.state = {
quote: {
ticker: '',
ask: '',
bid: '',
isChanged: ''
}
handleQuoteFetched (data) {
// here Im looking for a way to compare data and quote
// and identify values changes and set isChanged Yes/No
this.setState({
quote: data
})
}
render () {
//...
<td className={quote.isChanged === 'Yes'?this.state.classCSSHighlight:''}>
{quote.rateBid}>
</td>
}
I think you want to use the setState callback function.
this.setState((prevState, newState) => {
// do comparison here
})
You can use lodash.isEqual function to do a deep comparison between 2 values.
handleQuoteFetched (data) {
// here Im looking for a way to compare data and quote
// and identify values changes and set isChanged Yes/No
this.setState(prevState => {
const nextState = _.isEqual(prevState.quote, data) ? {} : {...data, ISCHANGED: true}
return nextState
})
}
In my case, I'm using React.js and I would like to dynamically update the values in the deployOptions object.
For example -
initial state looks like:
getInitialState() {
return {
deployOptions: {
config: null,
action: 'deploy',
env: 'qa'
}
}
}
Obviously this is not correct - but this is what I'm trying to achieve
configOptionChange(option) {
// option -> { key: 'env', value: 'prod' }
this.setState({
[deployOptions.option.key]: option.value
});
}
so that my state would then be
{
deployOptions: {
config: null,
action: 'deploy',
env: 'prod' // only this changes
}
}
It's not especially pretty, but I think this is the best you can do with ES6:
configOptionChange({ key, value }) {
this.setState({
...this.state,
deployOptions: {
...this.state.deployOptions,
[key]: value
}
});
}
It's basically equivalent to your own Object.assign solution but using the ES6 spread (...) operator (and argument destructuring for good measure).
Here's a second option that isn't as clever but feels a little cleaner to me:
configOptionChange({ key, value }) {
const { deployOptions: prevDeployOptions } = this.state;
const deployOptions = { ...prevDeployOptions, [key]: value };
this.setState({ ...this.state, deployOptions });
}
Just like these are nested objects, you can nest ES6 Computed Property Name like this:
[yourNestedObject]: {...yourNestedObject, [nestedObjectProperty]: value}
Here is an example function that will update an object's properties and the properties of nested objects. Where prop is the property to change, value is the value to assign, and propObj is the name of your nested object. I used useState hook for this.
const [obj, setObj] = useState({
prop1: '',
prop2: '',
nestedObj: {
prop1: '',
prop2: ''
}
});
const updateObj = (prop, value, propObj=false) => {
if (propObj) {
setObj({...obj, [propObj]: {...obj[propObj], [prop]: value}})
} else {
setObj({...obj, [prop]: value})
}
}
I think this might do the trick - but if anyone has a better solution?
configOptionChange(option) {
this.setState({
deployOptions: Object.assign({}, this.state.deployOptions, {[option.key]: option.val})
});
}
I'm using reduce to update the state of a component from a passed string of path properties:
handleChangeNested(event) {
const target = event.target; //an <input /> tag
let newState = { ...this.state };
let valuePath = target.name.split('.'); //the input's name is the the keys of the state object like 'group1.input1'
// Move down in the state object until we get to the 'bottom' property and change its value
// If the object does not have the properties we create them on the fly
valuePath.reduce((acc, key, index, path) => {
if (index === path.length - 1) {
acc[key] = target.value;
}
acc[key] = acc[key] || {};
return acc[key];
}, newState);
this.setState(newState);
}
The best solution I know would be to use "immutable" library.
configOptionChange(option) {
// option -> { key: 'env', value: 'prod' }
this.setState(state => setIn(state, ['deployOptions', option.key], option.value));
}