ReactJs: How to update property of state - javascript

I am trying to update the isSelected property for a row of my data stored in the state, the property doesn't update can anyone please tell me the best way to do this?
var selectedIdsPush = [];
var selectedIdsPush = this.state.contacts.slice();
for(var i=0;i<selectedIdsPush.length;i++)
{
var idAsNumber = parseInt(id);
if (page.state.contacts[i].id === idAsNumber) {
page.state.contacts[i].isSelected = true;
break;
}
}

Reacts wants consumers to setState instead of assigning properties directly. In this example, we could build and assign a new contacts list using:
var idAsNumber = parseInt(id);
var newContacts = this.state.contacts.map(function (contact) {
if (contact.id === idAsNumber) {
contact.isSelected = true;
}
return contact;
});
this.setState({ contacts: newContacts });

The property probably does get updated, but it won't be reflected in the UI as your app isn't re-rendered.
You could call this.forceUpdate() to force a re-render, https://facebook.github.io/react/docs/component-api.html#forceupdate.
Or more likely you should use this.setState(), https://facebook.github.io/react/docs/component-api.html#setstate.
I currently struggle with when/where to use state as apparently it's not advised. Search for react avoid state for more information.

Related

How to update or add an array in react.js?

I'm using react native and my component is based on class base function. I'm facing difficulty in updating or adding object in an array..
My case :
I have an array:
this.state = {
dayDeatil:[]
}
now i want to add an obj in it but before that i want check if that object exist or not.
obj = { partition :1, day:"sunday","start_time","close_time",full_day:false}
in condition i will check partition and day if they both not match. then add an object if exist then update.
here is function in which i'm trying to do that thing.
setTimeFunc =(time)=>{
try{
console.log("time.stringify() ")
let obj = {
partition:this.state.activePartition,
day:this.state.selectedDay.name,
full_day:false,
start_time:this.state.key==="start_time"?time.toString():null
close_time:this.state.key==="close_time"?time.toString():null
}
let day = this.state.dayDetails.filter((item)=>item.day===obj.day&&item.partition===obj.partition)
if (day.length!==0) {
day[this.state.key]=time.toString()
this.setState({...this.state.dayDetail,day})
} else {
console.log("2")
this.setState({
dayDetails: [...this.state.dayDetails, obj]
})
}
this.setState({ ...this.state, clockVisiblity: false });
}
catch(e){
console.log("error -> ",e)
}
}
To check if the object exists or not, you can use Array.find() method, if it doesn't exists, the method will return undefined.
Now to update the state, the easier way would be to create a new array and set it as the new dayDetails state like this:
const { dayDetails } = this.state;
dayDetails.push(newData);
this.setState({ dayDetails: [...dayDetails] })
You should use the spread operator when setting the state because React uses shallow comparision when comparing states for a component update, so when you do [...dayDetails] you're creating a new reference for the array that will be on the state, and when React compares the oldArray === newArray, it will change the UI.
Also, after your else statement, you're updating the state with the state itself, it's good to remember that React state updates are asynchronous, so they won't be availabe right after the setState function call, this may be causing you bugs too.

How to make props equal to state that does not exist yet?

Solved Thank you for your help
I am setting props of component
<Component myprops={state_variable}/>
The problem is that when I am creating the component and setting the props the state variable does not exist yet and my code breaks. What can I do to solve this problem? In addition when I change the state the prop is not updated.
<ServiceTicket
showOverlay={Tickets_disabled_withError[ticket_num]?.isDisabled}
showSelectedError={Tickets_disabled_withError[ticket_num]?.showError}
/>
My intial state initial variable:
const [Tickets_disabled_withError,setTickets_disabled_withError] = useState({})
I am trying to call function that will update state and change value that props is equal to.
const OverLayOnAll = (enable) =>
{
let tempobject = Tickets_disabled_withError
for (let key in tempobject)
{
if (enable == "true")
{
tempobject[key].isDisabled = true
}
else if (enable == "false")
{
tempobject[key].isDisabled = false
}
}
setTickets_disabled_withError(tempobject)
}
I fixed the issue. Thank you so much for your help. I had to set use optional chaining ?. and also re render the component.
The value exists. It's just that the value itself is undefined. You need to set an initial value when defining your state
const [statevariable, setstatevariable] = useState({
somekey: {
isDisabled: false // or whatever the initial value should be
}
}) // or whatever else you need it to be
For your second problem, you are using the same pointer. JavaScript does equality by reference. You've transformed the existing value, so React doesn't detect a change. The easiest way to fix this is to create a shallow copy before you start transforming
let tempobject = {...Tickets_disabled_withError}
Your question isn't very clear to me, but there's a problem in your setTickets_disabled_withError call.
When you update a state property (ticketsDisabledWithError) using its previous value, you need to use the callback argument.
(See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous)
overlayAll = (enable)=> {
setTicketsDisabledWithError((ticketsDisabledWithError)=> {
return Object.keys(ticketsDisabledWithError).reduce((acc,key)=> {
acc[key].isDisabled = (enabled=="true");
return acc;
}, {}); // initial value of reduce acc is empty object
})
}
Also, please learn JS variable naming conventions. It'll help both you, and those who try to help you.

Why isn't the mobx #computed value?

Simple: the computed value isn't updating when the observable it references changes.
import {observable,computed,action} from 'mobx';
export default class anObject {
// THESE WRITTEN CHARACTERISTICS ARE MANDATORY
#observable attributes = {}; // {attribute : [values]}
#observable attributeOrder = {}; // {attribute: order-index}
#observable attributeToggle = {}; // {attribute : bool}
#computed get orderedAttributeKeys() {
const orderedAttributeKeys = [];
Object.entries(this.attributeOrder).forEach(
([attrName, index]) => orderedAttributeKeys[index] = attrName
);
return orderedAttributeKeys;
};
changeAttribute = (existingAttr, newAttr) => {
this.attributes[newAttr] = this.attributes[existingAttr].slice(0);
delete this.attributes[existingAttr];
this.attributeOrder[newAttr] = this.attributeOrder[existingAttr];
delete this.attributeOrder[existingAttr];
this.attributeToggle[newAttr] = this.attributeToggle[existingAttr];
delete this.attributeToggle[existingAttr];
console.log(this.orderedAttributeKeys)
};
}
After calling changeAttribute, this.orderedAttributeKeys does not return a new value. The node appears unchanged.
However, if I remove the #computed and make it a normal (non-getter) function, then for some reason this.orderedAttributeKeys does display the new values. Why is this?
EDIT: ADDED MORE INFORMATION
It updates judging by logs and debugging tools, but doesn't render on the screen (the below component has this code, but does NOT re-render). Why?
{/* ATTRIBUTES */}
<div>
<h5>Attributes</h5>
{
this.props.appStore.repo.canvas.pointerToObjectAboveInCanvas.orderedAttributeKeys.map((attr) => { return <Attribute node={node} attribute={attr} key={attr}/>})
}
</div>
pointerToObjectAboveInCanvas is a variable. It's been set to point to the object above.
The changeAttribute function in anObject is called in this pattern. It starts in the Attribute component with this method
handleAttrKeyChange = async (existingKey, newKey) => {
await this.canvas.updateNodeAttrKey(this.props.node, existingKey, newKey);
this.setState({attributeEdit: false}); // The Attribute component re-renders (we change from an Input holding the attribute prop, to a div. But the above component which calls Attribute doesn't re-render, so the attribute prop is the same
};
which calls this method in another object (this.canvas)
updateNodeAttrKey = (node, existingKey, newKey) => {
if (existingKey === newKey) { return { success: true } }
else if (newKey === "") { return { success: false, errors: [{msg: "If you'd like to delete this attribute, click on the red cross to the right!"}] } }
node.changeAttribute(existingKey, newKey);
return { success: true }
};
Why isn't the component that holds Attribute re-rendering? It's calling orderedAttributeKeys!!! Or am I asking the wrong question, and something else is the issue...
An interesting fact is this same set of calls happens for changing the attributeValue (attribute is the key in anObject's observable dictionary, attributeValue is the value), BUT it shows up (because the Attribute component re-renders and it pulls directly from the node's attribute dictionary to extract the values. Again, this is the issue, an attribute key changes but the component outside it doesn't re-render so the attribute prop doesn't change?!!!
It is because you have decorated changeAttribute with the #action decorator.
This means that all observable mutations within that function occur in a single transaction - e.g. after the console log.
If you remove the #action decorator you should see that those observables get updated on the line they are called and your console log should be as you expect it.
Further reading:
https://mobx.js.org/refguide/action.html
https://mobx.js.org/refguide/transaction.html
Try to simplify your code:
#computed
get orderedAttributeKeys() {
const orderedAttributeKeys = [];
Object.entries(this.attributeOrder).forEach(
([attrName, index]) => orderedAttributeKeys[index] = this.attributes[attrName])
);
return orderedAttributeKeys;
};
#action.bound
changeAttribute(existingAttr, newAttr) {
// ...
};
Also rename your Store name, Object is reserved export default class StoreName

Not able to change state on event trigger in react native

I am calling an event on react-native-voice to recognize the speech and calling an API and setting the state. But when I am setting a new value to a state array the array only have the new value instead of having the previous value. This is my code
constructor(props){
super(props);
this.state = {
results = [];
}
Voice.onSpeechResults = this.onSpeechResults;
}
and I have a event which is been triggered to call an API like this.
onSpeechResults = e => {
this._startRecognizing();
//var joined = this.state.results.concat();
getProductListingService(e.value[0]).then(data=>{
let demoVar = Object.values(data);
var newArray = this.state.results;
newArray.push(demoVar[1]);
this.setState({
results:newArray
})
this.setState({
show:true
});
});
};
But when setting the state I am only able to get the new element been pushed into the array instead of the previous state. I have tried using concat, spread, and setting a new array. Stuck, Please help. :(
First declare your state like:
this.state = {
results: []; // Don't use = for properties
}
second notice that if you setState() with same values that it already has, it doesn't set the state(and call render function).

React.js - update radio or check input on re-render

I have started writing a simple UI in React, and re-rendering input fields after an ajax call works fine on all components (text, select), except radio groups and checkboxes.
I can't for the life of me get checkboxes or radio groups to be correctly checked on a re-render after the "value" prop has been changed on a parent.
I know that the component is getting re-rendered from console output, and I also now that the checked/selected state is being calculated correctly. However this is never reflected in the UI, which remains in the state it was on the initial render.
Code is below:
//Mixin used for all form inputs
var InputField = {
isValid : function(){
var input = this.state.value;
var valid = true;
if(this.props.validations !== undefined){
for(var i = 0; i < this.props.validations.length;i++){
if(valid){
valid = this.props.validations[i](input);
}
}
}
this.setState({isValid: valid});
return valid;
},
componentWillReceiveProps: function(newProps){
this.setState({value: newProps.value});
},
getInitialState: function() {
return {value: this.props.value, isValid: true};
}
};
//the actual component:
var RadioButtons = React.createClass({
mixins: [InputField],
handleChange: function(){
var props = this.props;
var selectedOpts = _.map($(React.findDOMNode(this.refs.radiobuttons)).find(':checked'), function(opt){ return parseInt(opt.value); });
var values = _.map(selectedOpts, function(index){
return props.options[index];
});
if(values.length > 0)
this.setState({value: values[0]});
else
this.setState({value: null});
},
render: function(){
var showProp = this.props.show;
var i = 0;
var self = this;
var options = _.map(self.props.options,function(opt){
var selected = show(self.state.value,showProp) === show(opt,showProp);
console.log(selected);
var result = (<label className="radio">
<span className="checked"><input type="radio" name={self.props.name} value={i} checked={selected} onChange={self.handleChange}>{show(opt,showProp)}</input></span>
</label>);
i = i + 1;
return result;
});
return ( <FormField label={this.props.label} fieldClass="col-md-8" ref="radiobuttons" errorMessage={this.props.errorMessage} isValid={this.state.isValid}>
{options}
</FormField>
);
}
});
I am somewhat baffled by this, and would appreciate any help..
edit
It seems every DOM element and value is set correctly, the only thing that is not happening is it being reflected visually.
Suspect this might be a general browser/JS issue rather than specifically React?
I can't for the life of me get checkboxes or radio groups to be
correctly checked on a re-render after the "value" prop has been
changed on a parent
That's because getInitialState is only called for the first render. And that's why you should avoid passing the initial state as a prop. Here's an answer of mine regarding the same topic.
If you want future renders to reflect the new props being passed in, do not store their values in the state.
React has the concept of Controllable and Uncontrollable components.
Controllable components store very little state. They receive virtually everything as a prop and they expose eventHandlers to inform the parent when the "state" changes so the parent can render them again. As they don't store much state, they can't just setState to rerender. The parent has to do it.
Uncontrollable components will work for themselves. They store state, and when you change them, they are able to rerender without the intervention of their parents. Future rerenders of uncontrollable components will not have much effect on their state.
Your Radio is acting as an uncontrollable component. And that's why it's not responding to prop changes.
It turns out this is not an issue with React, nor with the code above:
It seems jquery.uniform.js, which is on the page somehow interferes with showing check and radio box updates properly.
Nasty.

Categories

Resources