Using variable in setState - react - javascript

I know that similar questions were asked before, but what if i don't want to set the entire state, only one of its properties, to a variable? Something like this:
var initialProperty = {
name: '',
id: 0,
type: ''
}
class Example extends React.Component{
constructor(props){
super(props);
this.state = {
otherProperty: '',
targetProperty: initialProperty
//at the start of the Component, everything works fine.
//the targetproperty is set to initialproperty
}
}
//by the time, the targetproperty was changed from the initial in the component
//but if i click a button, i want to set targetproperty to the initialproperty
somethingHappensOnClick = () => {
this.setState({targetProperty: initialProperty})
//unfortunately, the targetproperty wasn't set to the initial.
}
}
Am I doing something wrong? Why targetProperty doesn't change?

This is happening because, in js array and object get copied by reference. So when you are setting
targetProperty: initialProperty
targetProperty will get the reference of initialProperty, and all the changes that you will do to targetProperty, will get applied to initialProperty as well.
Idea is, create a new object each time, instead of copying the reference.
Write it like this:
var initialProperty = {
name: '',
id: 0,
type: ''
}
class Example extendds React.Component{
constructor(props){
super(props);
this.state = {
otherProperty: '',
targetProperty: Object.assign({}, initialProperty) // or {...initialProperty}
}
}
somethingHappensOnClick = () => {
this.setState({targetProperty: Object.assign({}, initialProperty)})
}
}

When you are setting targetProperty : initialProperty what happens is
initialProperty--->some_memory_location_x
//after doing targetProperty: initialProperty
targetProperty---->some_memory_location_x
so when you are mutating the targetProperty you are actually changing the values in the memory some_memory_location_x which is where your initialProperty is also pointing at so when you setState again, your targetProperty value doesn't change so try doing the way #Mayank Shukla Pointed out so that you don't mutate values

Related

React setState does not update a state array value

I am trying to change the state in a class component by using setState.
More specific I have a table, and I want to edit/update one of its elements. For this case, I am passing the indeces to the handleTableFieldOnChange function for the position of the value in the array.
Since I know that I should not mutate the state, I used an external library to deep copy the tables array/list.
The deep copy and the new value assignment works. The deep copy worked also with the JSON.parse(JSON.stringify(this.state.tables)); alternative.
Problem: For some reason the this.setState(...) does not change the tables value.
I do know the setState is asynchronous, this is why I used the callback and within it, the console.log(...) to check the updated value.
console.log(...) still emits the old value.
private handleTableFieldOnChange(val: boolean | string | number | [number, string], tblRowIndex: number, tblIndex: number, tblColINdex: number) {
const cloneDeep = require('lodash.clonedeep');
const newTables = cloneDeep(this.state.tables);
if (newTables && newTables[tblIndex] && newTables[tblIndex].items ) {
newTables[tblIndex].items![tblRowIndex][tblColINdex].value = val;
}
this.setState( {tables: newTables}, () => {
console.log(this.state.tables)
})
}
state: State = {
tables: [],
report: this.props.report,
};
constructor(props: DetailProp, state: State) {
super(props, state);
this.initFieldsAndTabels();
}
private initFieldsAndTabels() {
if (this.state.report && this.state.report.extraction_items) {
this.state.tables = [];
this.state.report.extraction_items.forEach((extractionItems) => {
this.state.tables.push(extractionItems);
});
}
}
The code in handleTableFieldOnChange looks fine to me.
However in initFieldsAndTabels you are applying push on state directly instead of calling setState which may probably cause the issues:
this.state.report.extraction_items.forEach((extractionItems) => {
this.state.tables.push(extractionItems); //#HERE
});
Also as React.Component docs state you should not call setState in constructor (you are calling initFieldsAndTabels in constructor. Instead you could use componentDidMount.
P.S. If you want to add those extraction items in the constructor then you need something like this:
constructor(props) {
super(props);
// method should return a new array/object, but not modify state
const tables = this.initFieldsAndTabels();
this.state = {
tables,
}
}

Passing all state properties to function except one

Is it possible to pass all state except one property? I mean I have component which looks like that, and I wish to pass to redux function this.props.editAnimal() all state except property "submitted", is it possible to somehow exclude one state property and pass all the others?
export class EditAnimal extends Component {
constructor(props) {
super(props)
this.state = {
animalName: '',
animalNumber: '',
animalChip: '',
animalAge: '',
extraInfo: '',
submited: false
}
}
handleSubmit = (e) => {
const id = this.props.id
e.preventDefault();
console.log(this.state);
this.props.editAnimal(id, this.state)
}
There are many ways of doing this. Here is one:
const tempState = {...this.state};
delete tempState.submited;
this.props.editAnimal(id, tempState);
First, this creates a copy of the state by destructuring the state into a temporary variable tempState. Then, we remove the property we don't want from the temporary variable with delete.
Sidenote: you have misspelled "submitted", it's with double "t".
If you are transpiling your code with babel, you can destructure the state and copy the variables you only care about:
const { submited, ...editAnimalParams } = this.state;
this.props.editAnimal(id, editAnimalParams)
You can see here what babel transpiles down to, but basically skips any keys you don't want to copy.
If you're not using babel (probably unlikely), then you can do a bit more verbosely but wouldn't need babel:
const editAnimalParams = Object.assign({}, this.state);
delete editAnimalParams.submited;
this.props.editAnimal(id, editAnimalParams)

setState does not update state

why this.setState does not work.
constructor(props) {
super(props);
this.state ={
obj: [],
}
}
,
componentDidUpdate(prevProps){
const { obj } = this.props;
this.setState({obj});
}
}
If you look at your dev console you'll see an error when that console log is supposed to occur, because you're using a "normal" function without any kind of this preservation. Instead, once that function gets called the this identifier will be referencing the global context (e.g. window), rather than your component.
Use an arrow function to make sure this is the one you actually meant, and you'll see that setState worked just fine:
componentDidUpdate(prevProps){
this.setState({
siteDataState: this.props.siteData
}, () => {
console.log(this.state.siteDataState);
});
}
That said, this is going to cascade-trigger because you're changing the component in the function that triggers when the component changes, so put some if code in there that makes sure that this.state.siteDataState isn't already what's in this.props.siteData.

How to create more states in react? Is the number of properties of react state fixed?

I understand I could simply add properties together in one go. this.state={a:'1','b':2} . But what if we want to add extra properties to the state object, not just in the constructor, or if the properties need to be added on a condition.
Is it only supposed to work on a fixed number or properties? (I am aware of dictionary and arrays as elements of a state. )
constructor(){
super();
this.state = { item: 'first item' }
this.state = { item2: 'second item' } // Or += append are not accepted.
}
Apparently, a React state is a JS Object So, this.state.item2= 'new item'; works.
Answered by Andy Ray and Mark E in comments below. The other portion of the question: how to dynamatically create state is added by
The Reason and marked it as answered
Thanks, everyone
You can have dynamic states like so:
class DynamicStateBasedOnProps extends React.Component{
constructor(props){
super();
this.state = { static: 'I am static' };
}
componentDidMount(){
const stateBasedOnProps = Object.keys(this.props)
.map(key => ({[key]: this.props[key]}));
this.setState(dynamicState)
}
render() {
return <div>
{JSON.stringify(this.state)}
</div>;
}
}
You are able to modify this.state object outside of a constructor function through this.setState({...}) and it is totally fine to add there new properties because it's just an JS object. Though, be aware, that every modification of the this.state leads to render method call.
More on this - https://reactjs.org/docs/state-and-lifecycle.html

getInitialState() replacing existing state. How to pass existing state in React?

I'm making some buttons that will show a certain class depending on the status from the database.
I've passed my API results through an emitter and into my state using the below:
constructor(props) {
super(props);
this.state = {
tickets: [],
user: []
};
this.onGetAllTickets = this.onGetAllTickets.bind(this);
this.onGetCurrentUser = this.onGetCurrentUser.bind(this);
}
However, I'm having to set a new state in my button using the getInitialState() option. My button looks like this:
const AssignButton = React.createClass({
getInitialState() {
return {
isPressed: false,
completeStatus: "Assign"
};
},
handleClick() {
this.setState({
isPressed: true,
completeStatus: "Assigned",
});
},
render () {
var assignClass = 'ui orange right floated button assign';
//THIS IS WHERE I AM TRYING TO CHECK TICKET STATUS
if(this.state.tickets.status === 2) assignClass = 'ui green right floated button done disabled';
else if(this.state.isPressed) assignClass += ' disabled';
return <button className={assignClass} onClick={this.handleClick}>{this.state.completeStatus}</button>;
}
});
However, passing the this.state.tickets.status results in the following:
TicketeesBoard.js:123 Uncaught (in promise) TypeError: Cannot read property 'status' of undefined(…)
I'm assuming this is because my state is overwritten by the getInitialState() method.
The this.state.tickets.status etc. works outside of this AssignButton component.
How do I pass my tickets from the original state into the getInitialState() method?
The problem is that you're trying to access the state of a different component. Your'e addressing this.state.tickets.status where ticket state is not declared in AssignButton
You've got two components. TicketBoard & AssignButton.
Your setting the tickets state in TicketBoard and you're trying to access it in AssignButton
Try passing down the tickets state to the assignButton via props and the change your conditions from this.state.tickets.status to this.props.tickets.status.
In addition, make sure that you won't have problems with .status being called on an empty array...

Categories

Resources