How to invoke onChange for radio button using react-bootstrap? - reactjs

I cannot find documentation on how to invoke onChange for radio buttons using react-bootstrap. I left out the button which submits the form. It works with every other field, like text inputs, so I left that out.
For each, teacherRate and overallRate, each radio button has a value of 1, 2, 3 but I am not sure how to tie that in.
I also know that I cannot have the values be the same for each category.
I am not looking to do Button Groups.
I looked online for similar answers but cannot find any. There was one person who posted their problem like mine but answered later saying he implemented react-bootstrap but did not post his solution. I cannot reply as I do not have enough points.
class Assignment extends Component {
constructor(props){
super(props)
this.state = {
form: {
teacherRate: '',
overallRate: ''
}
}
}
handleChange(event){
const formState = Object.assign({}, this.state.form)
formState[event.target.name] = event.target.value
this.setState({form: formState})
}
render() {
return (
<Grid>
<form>
<FormGroup>
<ControlLabel id='adminRate'>Rate the Teacher</ControlLabel>
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>1</Radio>{' '}
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>2</Radio>{' '}
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>3</Radio>{' '}
</FormGroup>
<FormGroup>
<ControlLabel id='adminRate'>Overall Rating</ControlLabel>
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>1</Radio>{' '}
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>2</Radio>{' '}
<Radio name='adminRate' type='integer' inline
onChange={this.handleChange.bind(this)}
value={this.state.form.adminRate}>3</Radio>{' '}
</FormGroup>
</form>
</Grid>
);
}
}

The onChange event is being fired, but your handleChange function is not doing what you are expecting.
handleChange(event){
const formState = Object.assign({}, this.state.form)
formState[event.target.name] = event.target.value
this.setState({form: formState})
}
If you take a look in a debugger, event.target does not have a name or value attribute for a check box. It simply has a checked attribute. You will have to find another way to get the information you are looking for out of the target.

To piggyback off the previous poster, in your Radio buttons you have value={this.state.form.adminRate} but adminRate is never defined in
this.state = {
form: {
teacherRate: '',
overallRate: ''
}
So when you return formState[event.target.name] = event.target.value from your handleChange function its not inputting any value into the event.target.name. So you may just want to input 1, 2, and 3 as the value in the corresponding buttons.
Also even if it did input values, event.target.name in your Radio buttons are adminRate so you'd run into the this.state problem again, so you'd have to convert name={adminRate} to name={teacherRate} in the first FormGroup buttons so that the formState object in
handleChange(event){
const formState = Object.assign({}, this.state.form)
formState[event.target.name] = event.target.value
this.setState({form: formState})
}
points to the teacherRate property inside the form object defined in this.state when you call this.setState({form: formState}) at the end of the function.
I don't know the relevance of using the 'Overall Rating' radio buttons so I can't assist with the second FormGroup but it would basically be the same logic as the first. You'd need Math logic to get the average number for the overall rating though.

You need to set the checked attribute on the <Radio> element as a response to some state/props, the following is a snippet from a Formik / react-bootstrap form:
<Radio
name="upload_radio"
checked={status.upload_radio === 'textarea'}
value="textarea"
onChange={e => setStatus({ upload_radio: e.target.value })}
>

Related

How do i default check a radio button in react?

I am trying to default check the first radio button which the following code helps me to do. When loaded the page the first radio button is checked but the problem i am facing is that it doesn't allow me to check the other buttons that also are present in the array.
constructor(props: any) {
super(props);
this.state = {
selectedSort: '',
sort: ['Apple', 'Orange '],
}
}
this.state.sort.map((sortType:string, index:number) => {
return <span key={`${sortType}${index}` onClick={() => this.setSort(sortType)} >
<input type="radio" id={sortType}
value={this.state.selectedSort}
name={sortType} defaultChecked={index===0}
}/>
<span>{sortType}</span>
})
private setSort = (selectedSort: string) => {
this.setState({
selectedSort: selectedSort
});
}
Issue
The defaultChecked value is a boolean but your condition sortType === 0 will always evaluate false since your sortType is only ever one of your sort state values, i.e. ["Apple", "Orange "].
Solution
If you want the first radio button to be default checked then you should compare against the mapped index.
defaultChecked={index === 0}
Other Issues & Suggestions
Radio button group inputs should all have the same name attribute.
Use a semantic label to wrap your inputs so they are more accessible.
Use the radio input's onChange event callback versus an onClick, to update state.
The sortType values alone should be sufficient for a React key.
Code:
{this.state.sort.map((sortType, index) => (
<label key={sortType}>
<input
type="radio"
id={sortType}
value={selectedSort}
name="sortType"
defaultChecked={index === 0}
onChange={(e) => this.setState({ selectedSort: e.target.id })}
/>
{sortType}
</label>
))}
Additionally, I suggest converting this to a fully controlled input since you have already all the parts for it. Remove the value attribute and use the checked prop. Set what you want the initial checked state to be. This will allow you have have already valid checked state.
state = {
selectedSort: 'Apple',
sort: ['Apple', 'Orange '],
}
{this.state.sort.map((sortType, index) => (
<label key={sortType}>
<input
type="radio"
id={sortType}
name="sortType"
checked={sortType === this.state.selectedSort}
onChange={(e) => this.setState({ selectedSort: e.target.id })}
/>
{sortType}
</label>
))}
Demo

How to make invisible react-google-recaptcha, Formik and yup work together?

I'm trying to make invisible react-google-recaptcha, Formik and yup to work together. The documentation says we should call recaptchaRef.current.execute() on form submission, but if we use Formik and yup together, it will trigger the submission logic only after all fields passed the validation schema.
Basically, we need to call the execute method, update the recaptcha value and submit the form with the same trigger event. My problem is exactly that: I'm having to use two events (one for the execute method and update the recaptcha + one to submit the form).
Check this sandbox: https://codesandbox.io/s/keen-mahavira-ont8z?file=/src/App.js
As you can see, the form is submitted only with the second click in the submit button...
With Formik, there are some ways to do background work for your form's. This basically could be achieved with handleChange or handleBlur props being passed to the form component.
For instance, I am sure you would have other inputs in your form elements and not just a captcha (if it's just a captcha in the form, then do let me know! - this can also be solved)
So when you have other elements, you can ensure to use some of the Formik's API to handle the automatic trigger:
handleBlur event to trigger the ReCaptcha
isSubmitting to control the submit button's state
setSubmitting event to manipulate the button's state
As I see, there are a lot of ways to handle this through their API's: https://formik.org/docs/api/formik
The way I tried to achieve it is by adding a listener for onBlur on all fields and then checking if reCaptcha value is present or not. Based on that I trigger the execute the captcha and ensure to set the submitting value as true:
const handleBlur = (e) => {
console.log("$$$$", props.isSubmitting);
if (!props.values.recaptcha) {
this._reCaptchaRef.current.execute();
props.setSubmitting(true);
}
props.handleBlur(e);
};
Here is the CodeSandbox Link: https://codesandbox.io/s/silly-saha-qq7hg?file=/src/App.js
This shows the working model of handling onBlur of a field and triggering it in the background. If you notice, you can also disable and enable the submit button using isSubmitting and setSubmitting.
Also setting validateOnChange={false} and validateOnBlur={false}, because there is no need to validate on change or blur for captcha.
Pasting code here just in case for you to glance:
import React, { Component, createRef } from "react";
import ReCAPTCHA from "react-google-recaptcha";
import { Formik } from "formik";
import * as yup from "yup";
const TEST_SITE_KEY = "6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI";
export default class MyForm extends Component {
constructor(props) {
super(props);
this._validationSchema = yup.object().shape({
recaptcha: yup.string().required(),
name: yup.string().required(),
address: yup.string().required()
});
this._initialValues = { recaptcha: "", name: "", address: "" };
this._reCaptchaRef = createRef();
}
render() {
return (
<Formik
validationSchema={this._validationSchema}
initialValues={this._initialValues}
validateOnChange={false}
validateOnBlur={false}
onSubmit={(values) => console.log(values)}
>
{(props) => {
const handleBlur = (e) => {
console.log("$$$$", props.isSubmitting);
if (!props.values.recaptcha) {
this._reCaptchaRef.current.execute();
props.setSubmitting(true);
}
props.handleBlur(e);
};
return (
<form onSubmit={props.handleSubmit}>
<label>Name: </label>
<input
type="text"
onChange={props.handleChange}
value={props.values.name}
name="name"
onBlur={handleBlur}
/>
<label>Address: </label>
<input
type="text"
onChange={props.handleChange}
value={props.values.address}
name="address"
onBlur={handleBlur}
/>
<ReCAPTCHA
ref={this._reCaptchaRef}
sitekey={TEST_SITE_KEY}
onChange={(value) => {
console.log("$$$$", props.isSubmitting, value);
props.setFieldValue("recaptcha", value);
props.setSubmitting(false);
}}
size="invisible"
/>
<button type="submit" disabled={props.isSubmitting}>
SUBMIT
</button>
{props.errors.name && <div>{props.errors.name}</div>}
</form>
);
}}
</Formik>
);
}
}

Passing Parameters to react-router-dom Link Dynamically

I am working on a React project where in one of the components I am passing properties to another component through react-router-dom Link like so:
let updateEmpInfo;
...
<Button component={Link}
halfWidth
variant="contained"
color="primary"
preventDefault
onClick={getEmpInfo}
to={{pathname: "/update",
state:
{id: updateEmpInfo.id,
name: updateEmpInfo.name,
department: updateEmpInfo.department,
gender: updateEmpInfo.gender}
}}>Update information
</Button>
My questin is specifically about the "state" property. As you can see, I am passing several state parameters (id, name, department and gender). Since Link component is inside the render method, it requires the updateEmpInfo variable to be defined somewhere in the render method. I am trying to take input from user and based on their input set the value of all updateEmpInfo properties, after the component has rendered. All of them will be passed to the state property of Link. I am trying to do so in the getEmpInfo function. However, regardless of the input, the state property preserves all initial values that were set during the render. Is there a way to change updateEmpInfo properties based on the user input once the Link is clicked? I my question is clear enough. I will be happy to provide any additional info. Thank you in advance!
If I understood you correctly, the values passed are not how the user adjusted them.
I'd suppose it's an onChange-Listener missing to the InputFields or an onSubmit to the whole form respectively.
import { Component } from 'react';
import { withRouter } from 'react-router-dom';
class MyEmployeeForm extends Component {
constructor(props) {
super(props);
this.state = {
id: "",
name: "",
department: "",
gender: ""
};
}
onChange = (event) => {
this.setState({[event.target.name]: event.target.value});
}
onUpdate = (event) => {
event.preventDefault();
this.props.history.push({
pathname: '/update',
state: this.state});
}
render() {
<form>
<input type="text" name="id" placeholder="id" value={this.state.id} onChange={this.onChange} />
<input type="text" name="name" placeholder="name" value={this.state.name} onChange={this.onChange}/>
<input type="text" name="department" placeholder="department" value={this.state.department} onChange={this.onChange}/>
<input type="text" name="gender" placeholder="gender" value={this.state.gender} onChange={this.onChange}/>
</form>);
<Button component={Link}
halfWidth
variant="contained"
color="primary"
onClick={this.onUpdate}>
Update information
</Button>
}
}
export default withRouter(MyEmployeeForm);
see here Get form data in ReactJS ,
How to pass params with history.push/Link/Redirect in react-router v4?

How to handle Formik's `handleChange` prop?

I get it that Field has an onChange attribute where you can pass the own Formik onChange prop from here: https://jaredpalmer.com/formik/docs/api/formik#handlechange-e-reactchangeevent-any-void.
However, I am struggling to understand where these value[key] is passed, so I can handle the data passed in the form. Found in withFormik(): How to use handleChange that I can pass two callbacks to Formik's onChange prop, but I wonder if there is a better way to handle this.
edit after comments from folks that replied, thanks for that:
My code using these 2 callbacks in the onChange prop in Field:
export default function FormikForm() {
const onSubmitHandler = (formValues, actions) => {
console.log(formValues);
console.log(actions);
};
const onChangeHandler = (e) => {
console.log(e.target.value);
};
return (
<div>
<h1>This is the Formik Form</h1>
<Formik
initialValues={{
name: "",
email: "",
age: ""
}}
onSubmit={onSubmitHandler}
render={props => {
return (
<Form>
<label>
Name
<Field
name="name"
type="text"
placeholder="name"
onChange={e => {props.handleChange(e); onChangeHandler(e)}}
/>
</label>
<button type="submit">Submit</button>
</Form>
);
}}
/>
</div>
);
}
Is there a way to do a similar thing as in onSubmitHandler, where Formik automagically outputs the value of the input without having to call these 2 functions in the onChange?
Thanks
Every field component receives a field prop which has a value prop containing the value for that field, as well as a form prop containing helper methods that you can use to set the value. I'd need to see the structure of your code to give specific suggestions on how to implement what you want, but you can emulate the default functionality by calling form.setFieldValue(field.name, field.value). In addition, the field prop has this handler built in by default in the field.onChange prop.

Handle State change in React with ONE handleChange method for both selectFields and TextFields

I would like to know how I can handle state changes with ONE handleChange method.
I handle two text fields with handleChange, but I cant figure out how I can handle changes on SelectField as well, with the same handleChange method.
When change the Spot Type from Kitesurfing to Diving and console.log this.state I get this:
form
:
description: "This is a amazing spot in Spain."
name: "Blue water"
spotType: "Kitesurfing"
undefined: undefined <-- this is from the SelectField, when changing from Kitesurfing to Diving..
AddASpot.js
import React, { Component } from "react";
import TextField from "material-ui/TextField";
import SelectField from "material-ui/SelectField";
import MenuItem from "material-ui/MenuItem";
import RaisedButton from "material-ui/RaisedButton";
class AddASpot extends Component {
constructor(props) {
super(props);
this.state = {
form: {
spotType: "Kitesurfing"
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange = event => {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
const form = Object.assign({}, this.state.form);
form[name] = value;
this.setState({ form });
};
handleSubmit = () => {
console.log(this.state);
};
render() {
return (
<div>
<h1>Add a new Spot</h1>
<TextField
name="name"
onChange={this.handleChange}
hintText="Name of Spot"
/>
<br />
<SelectField
floatingLabelText="Spot Type"
name="spotType"
value={this.state.form.spotType}
onChange={this.handleChange}
>
<MenuItem value="Diving" primaryText="Diving" />
<MenuItem value="Kitesurfing" primaryText="Kitesurfing" />
<MenuItem value="Surfing" primaryText="Surfing" />
<MenuItem value="Spearfishing" primaryText="Spearfishing" />
</SelectField>
<br />
<TextField
name="description"
onChange={this.handleChange}
hintText="Description of the Spot"
multiLine={true}
rows={3}
rowsMax={4}
/>
<br />
<RaisedButton
onClick={this.handleSubmit}
label="Add Spot"
primary={true}
/>
</div>
);
}
}
export default AddASpot;
Your logic inside of the handleChange method needs some reconsidering. It seems you are trying to make things a lot more complicated than they need to be. Your example is hard to reproduce, unless someone wants to build this themselves, which is probably why no one has answered it yet.
I did however get a similar type of example working in Codepen, without the material UI package you are using, as I do not know how to add those packages properly into Codepen.
To hopefully make this easier for you, I just got the select menu working for you, and ignored the other ones for now. Hopefully this will help you visualize how to incorporate the logic to handle the others properly.
Check the pen here. You'll see that I changed your handleChange method to two lines of code, without the need for using Object.assign.
If you can get a full working version of your specific example, I'd be glad to change the answer to reflect that, but like I said, it is not easy to reproduce for people on here.
handleChange = event => {
const value = event.target.value
this.setState({ form: { spotType: value } })
};
I did not figure out how to handle this with only one method so I created one unique for the SelectFields as this method requires three props.
handleChangeSelectField = (event, index, value) => {
const form = Object.assign({}, this.state.form);
form["spotType"] = value;
this.setState({ form });
};

Resources