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

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.

Related

Why won't the updated variable values display?

I am setting initial values on a these variables. I change the values, and they do log correctly, however in the return function, they only display the original values. How do I make sure they show the correct updated values?
I declare my variables:
let teamBillableData = [];
let teamMemberBillableAmount = 0;
let teamMemberIndex = 0;
Then I change the values:
teamBillableData = parsedData.results;
teamMemberIndex = teamBillableData.findIndex(function (teamMember) {
return teamMember.user_name === teamMemberName;
})
teamMemberBillableAmount = teamBillableData[teamMemberIndex].billable_amount;
When I log the variables, they are correct:
console.log(teamMemberIndex); <---- Returns correct new value of 1
console.log(teamMemberBillableAmount); <---- Returns correct new value of 1,221.25
However, when I render the values in my return function in my React app, they render the initial values stull:
return (
<div>
<div>
<div>
<h5>{teamMemberIndex}</h5> <---- Returns old original value of 0
<h5>${teamMemberBillableAmount.toLocaleString()} </h5> <---- Returns old original value of 0
</div>
</div>
</div>
);
};
I assume it's rendering before the values are changed. But I do not know how to make it render AFTER they values are changed.
A React component only re-renders after either its state or its props change.
If you want your component to re-render when a variable changes, you have to add said variable to the component's state
If you're dealing with functional components, look into useState hook.
As told by Michael and Shubham you can write a functional component that triggers a rerender each time the state is updated using the useState hook.
An Example could be:
import React, { useState } from "react";
function Example() {
const [teamMemberBillableAmount, setTeamMemberBillableAmount] = useState(0);// initial state
const clickHandler = () => setTeamMemberBillableAmount(teamMemberBillableAmount + 1); // add 1 to the teamMemberBillableAmount state each time the button is clicked (a rerender will happen)
return (
<div>
<p>The team billable amount is:<strong style={{ color: "red" }}> teamMemberBillableAmount}</strong></p>
<button onClick={clickHandler}>Press me to add +1</button>
</div>
);
}
You can run and play with this code here

How to Pass React form state from app to multiple components (functional)

I'm still fairly new to React, so I'm sorry if this is a repetitive post. I'm working with React to create a form that spreads across different pages. The idea is to have the type of form you'd receive from a job application. One that has multiple steps.
I have different components made for each step of the form. The first page is a home page. Imagine a button to take you to the create page. Then you see the Create page. There is a small form with an amount and name. When you click the next page, you'll see the Customize page. There you can edit your budget by priority.
Here in my poorly drawn picture, I have the app obviously, and a budget homepage to manage state across it's children. I figured I should do it this way because, well it's the only way I know how. The problems I have, are one: I don't know how to properly pass state throughout the components. Two: I don't even know if what I'm doing is correct. The React docs use forms in Class based components, which sort of helps, but I can't configure it to my problem.
Any help or suggestions are greatly appreciated. Also I want to show code from the Budget Page.
import React, { useState, useEffect, useRef } from "react";
import BudgetResultsPage from './BudgetResultsPage';
const [count, setState] = useState(0);
const handleStateCount = () => {
if(count>3) {count = 0;}
return setState(count + 1);
}
if(count === 0) {
console.log(`homepage state ${count}`)
return (
<div className={Styles.Calculator}>
<BudgetHomePage setState={count} handleStateCount={handleStateCount}/>
</div>
)
} else if(count===1) {
console.log(`budget create state ${count}`)
return (
<div className={Styles.Calculator}>
<CalculatorHeader />
<CreateBudget setState={count} handleStateCount={handleStateCount} setAmount={amount} handleAmount={handleAmount}/>
</div>
)}
There are obviously more imports for the other pages and more code passed in the component. This is just a snippet of the way I'm handling the components.
It's probably silly, but I'm creating a count in state, then when part of the form is submitted, the count goes up and it changes the page based on what the count is at. Works great for just one state, but I'm having problems with adding more state to it.
Thanks again!
I have written an example functional components illustrating some of your questions. I wrote this at home and only had notepad++ so might not compile if you copy paste. The comments will explain your questions
import React from 'react';
import Create_budget_page from './Create_budget_page.js';
const BudgetPage = props => {
// State 1: assigning one value to the state.
// State 2: assinging a list to the state.
// State 3: assinging a object to the state.
const [simple_state, set_simple_state] = useState(false);
const [list_state, set_list_state] = useState(["Hello","World","!"]);
const [object_state, set_object_state] = useState(
{
curmenu: "human_bios",
height: "196cm",
weight: "174lbs",
eye_colour: "blue",
hair_colour: "dirty_blonde"
}
);
// there are several possiblities, here's one with a list of objects
const [list_objects, set_list_objects] = useState(
[
{count: 69, wasClicked: false},
{count: 420, wasClicked: true},
// endless possibilities, the tricky part is properly correctly updating your states deep in the list
{integers: [1,5,2,3], keys: {isAlive: false, cur_menu: "config_menu"}}
]
);
// this function updates the state that stores an object
// arguments:
// new_values_object: the programmer passes in a object to the function with the values they want to update
// an example of calling this function in a child functional component:
// props.update_object_state({height: "165cm", weight: "143lbs", hair_colour: "black"});
function update_object_state(new_values_object){
set_object_state(prevState => {
const new_obj = prevState;
// loop through object keys and update the new_obj with the object passed in as a argument
for (var key in new_values_object){
new_obj[key] = new_values_object[key];
}
// returning the new_object will update the object_state
return new_obj;
})
}
// conditionally render based on state
switch(object_state["curmenu"]){
case "home_page":
return (
// pass in
// all props, 1 prop, state
<Create_budget_page {...props} parent_id={prop.parent_id} simple_state={simple_state} list_objects={list_objects}/>
) ;
break;
// pass in function
case "human_bios":
return (
<div className="error_page" onClick={() => {props.update_parent_state({error_occured: true})}}>This is an Error Page, click me to report diagnostics</div>
);
break;
// if none of cases are met default code executes
default:
// renders nothing
return null;
}
}
Which kind of problems are you experiencing? I assume that the part of code you left there is inside a component body (a function, as you are using functional way).
You should have no problems passing multiple props to child components even though you have to keep in mind other approaches (excesive number of props means something is wrong in general...)
Let me know if you have more questions.
EDIT:
Suppose you have that ResultsPage child component and in your parent component you do something like:
<ResultsPage someProp={someValue} someOtherProp={someOtherValue} />
Then in your child component you can manage those props like this:
const ResultsPage = (props) => {
// props is an object like this: { someProp: someValue, someOtherProp: someOtherValue}
... // and in the body of the function you can manipulate it however you want
}
Generally it is better to destructure your props variable in the param directly instead of always do props.someProp, etc. like this:
const ResultsPage = ({someProp, someOtherProp}) => { ... }
More info about destructure here

Whats the right way to manipulate a model instance in React?

So I have a React component that accepts an instance of a function constructor (a Car).
The component's job is to display information about the Car and manipulate it based on the Car's public interface (methods and properties).
In the example below, a child component should add an accident on button click.
Question: What is the right way for the child to manipulate properties of the Car instance? The root parent's state stores reference to the instance of the Car, and the children are able to manipulate the Car's properties (like .accidents), but see the various onChange examples for why I'm struggling to find the right React way to do this.
I'd like to avoid a heavy handed solution like Flux to store this state.
Any suggestions would be appreciated!
function Car(name, color) {
this.name = name;
this.color = color;
this.accidents = [];
}
const myCar = new Car('Ferrari', 'Red');
myCar.accidents.push('accident #1');
class Accident extends React.Component {
handleButton1 = () => {
const newAccident = 'accident type1 # ' + Math.floor(Math.random()*100);
this.props.onChange1(newAccident);
}
handleButton2 = () => {
const newAccident = 'accident type2 # ' + Math.floor(Math.random()*100);
this.props.onChange2(newAccident);
}
handleButton3 = () => {
const newAccident = 'accident type3 # ' + Math.floor(Math.random()*100);
this.props.accidents.push(newAccident);
this.props.onChange3();
}
handleButton4 = () => {
const newAccident = 'accident type4 # ' + Math.floor(Math.random()*100);
this.props.accidents.push(newAccident);
// This circumvents React's state management, so the parent doesnt
// rerender when its state changes.
}
render() {
return (
<div>
<button onClick={this.handleButton1}>
Add accident (onChange1)
</button>
<button onClick={this.handleButton2}>
Add accident (onChange2)
</button>
<button onClick={this.handleButton3}>
Add accident (onChange3)
</button>
<button onClick={this.handleButton4}>
Add accident (option 4)
</button>
<ul>
{this.props.accidents.map((a, i) => <li key={i}>{a}</li>)}
</ul>
</div>
)
}
}
class DisplayCard extends React.Component {
state = {
editingCar: this.props.car
}
// Push the new accident into state and set it with the same reference.
onChange1 = (newAccident) => {
this.state.editingCar.accidents.push(newAccident);
// Is this semantically different than calling this.forceUpdate?
this.setState({
editingCar: this.state.editingCar,
});
}
// Clone the existing state we want to update and explicitly set that new state
onChange2 = (newAccident) => {
const newAccidentList = _.cloneDeep(this.state.editingCar.accidents);
newAccidentList.push(newAccident);
// Setting our new accident list like this converts editingCar to a POJO
// editingCar.name is lost because a deep merge does not happen.
this.setState({
editingCar: {
accidents: newAccidentList
},
});
}
// Just force update - this.state.editingCar was manipulated by <Accident />.
onChange3 = () => {
this.forceUpdate();
}
render() {
return (
<div>
<div>Car Name: {this.state.editingCar.name}</div>
<Accident
accidents={this.state.editingCar.accidents}
onChange1={this.onChange1}
onChange2={this.onChange2}
onChange3={this.onChange3}
/>
</div>
);
}
}
ReactDOM.render(
<DisplayCard car={ myCar } />,
document.getElementById('container')
);
Also on JSFiddle if you want to play around: https://jsfiddle.net/jamis0n003/fbkn5xdy/4/
EDIT: The React JS docs suggest integrating with "other libraries", such as Backbone models, using forceUpdate:
https://reactjs.org/docs/integrating-with-other-libraries.html#using-backbone-models-in-react-components
When state is stored in a parent component and a child component wants to manipulate that state, the parent should pass a callback function to the child's props. Then the child calls the callback to notify the parent to modify its own state. The child should never modify props since the change can have unintended consequences due to the way objects are referenced in JavaScript.
If you want to get really fancy, you can use Redux which stores "global" state in the top-most parent component. All child components issue (or dispatch) actions which notify the top-level parent to update its state which is then passed down again to all children components through their props.
What is the right way for the child to manipulate properties of the Car instance?
In general, rely on setState() to update state, which will reliably redraw the view, or if you mutate the data use forceRedraw() to ensure the view is redrawn with the latest data -- but using setState() is much preferred. In either case a child must notify a parent of a change using a callback like you have, but instead of having the child Accident actually change the data, make it a "dumb" component which notifies the parent of an intended change and the parent actually makes the change.
I'd like to avoid a heavy handed solution like Flux to store this state.
You may want to look into MobX, which is popular alternative to Flux/Redux that is a bit easier to get into because it allows you to mutate objects very much in the way you are already doing.

ReactJs: How to update property of state

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.

React - Updating state for each child component

Not sure if i'm understanding this correctly. If i'm making a child component, type button, that increments its own counter, is it possible to do so without having 2 separate functions? the following i've done seems like a hack? what if theres X amount of buttons, how would i refactor this code to be more dynamic?
REF's seem to work in the way i can reference the html in the child, but what about the other way? Am i even thinking about this the right way, because component has its own state, should it have its own update method?
/*** #jsx React.DOM */
var MyComponent = React.createClass({
getInitialState: function() {
return {
counter1: 1,
counter2: 1
};
},
increment: function(i) {
if (i === 1) {
this.setState({
counter1: this.state.counter1 + 1
});
} else {
this.setState({
counter2: this.state.counter2 + 1
});
}
},
render: function() {
return ( < div >
<ChildComponent item = {this.state.counter1} click={this.increment.bind(this, 1)}/>
<ChildComponent item={this.state.counter2} click={this.increment.bind(this, 2)}/ >
< /div>
);
}
});
var ChildComponent = React.createClass({
render: function() {
return (
<div>
<h1> Counter {this.props.item} </h1 >
<button onClick = {this.props.click} > ++ < /button>
</div >
);
}
});
React.render( < MyComponent / > , document.body);
I see in your comment that you've settled on putting state in the child component. While that works fine, the power of React is that you reason about state at a level that makes sense for all the interactions in the application. It is perfectly reasonable to hold both counters' state in the parent. I think the typical implementation would have a single update function in the parent, pass it, current counter value, and the counter IDs down as props, and have an update function in the child designed to invoke the parent's update function with the relevant counter ID.
Update: Your gist implementing this pattern is close to what I was talking about, keeping state in the parent and creating an onClick handler in the child that in turn calls its parent's update function, passing in parameters that let the parent know which counter to update. You may find it more useful to just pass the "refz" prop as that parameter rather than passing in the entire child React component, but as a proof of concept, you have the idea.

Categories

Resources