I am new to ReactJS, sorry if this sounds off. I have a component that creates several table rows according to the received data.
Each cell within the column has a radio checkbox. Hence the user can select one site_name and one address from the existing rows. The selection shall be shown in the footer. And thats where I am stuck.
var SearchResult = React.createClass({
render: function () {
var resultRows = this.props.data.map(function (result) {
return (
<tbody>
<tr>
<td>
<input type="radio" name="site_name" value={result.SITE_NAME}>
{result.SITE_NAME}
</input>
</td>
<td>
<input type="radio" name="address" value={result.ADDRESS}>
{result.ADDRESS}
</input>
</td>
</tr>
</tbody>
);
});
return (
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
{resultRows}
<tfoot>
<tr>
<td>chosen site name ???? </td>
<td>chosen address ????? </td>
</tr>
</tfoot>
</table>
);
},
});
In jQuery I could do something like $("input[name=site_name]:checked").val() to get the selection of one radio checkbox type and insert it into the first footer cell.
But surely there must be a Reactjs way, which I am totally missing? Many Thanks
Any changes to the rendering should be change via the state or props (react doc).
So here I register the event of the input, and then change the state, which will then trigger the render to show on the footer.
var SearchResult = React.createClass({
getInitialState: function () {
return {
site: '',
address: '',
};
},
onSiteChanged: function (e) {
this.setState({
site: e.currentTarget.value,
});
},
onAddressChanged: function (e) {
this.setState({
address: e.currentTarget.value,
});
},
render: function () {
var resultRows = this.props.data.map(function (result) {
return (
<tbody>
<tr>
<td>
<input
type="radio"
name="site_name"
value={result.SITE_NAME}
checked={this.state.site === result.SITE_NAME}
onChange={this.onSiteChanged}
/>
{result.SITE_NAME}
</td>
<td>
<input
type="radio"
name="address"
value={result.ADDRESS}
checked={this.state.address === result.ADDRESS}
onChange={this.onAddressChanged}
/>
{result.ADDRESS}
</td>
</tr>
</tbody>
);
}, this);
return (
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
{resultRows}
<tfoot>
<tr>
<td>chosen site name {this.state.site} </td>
<td>chosen address {this.state.address} </td>
</tr>
</tfoot>
</table>
);
},
});
jsbin
Here is the simplest way of implementing radio buttons in react js.
class App extends React.Component {
setGender(event) {
console.log(event.target.value);
}
render() {
return (
<div onChange={this.setGender.bind(this)}>
<input type="radio" value="MALE" name="gender"/> Male
<input type="radio" value="FEMALE" name="gender"/> Female
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Edited
You can use arrow function instead of binding. Replace the above code as
<div onChange={event => this.setGender(event)}>
For a default value use defaultChecked, like this
<input type="radio" value="MALE" defaultChecked name="gender"/> Male
Based on what React Docs say:
Handling Multiple Inputs.
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:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
render() {
return (
<div className="radio-buttons">
Windows
<input
id="windows"
value="windows"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Mac
<input
id="mac"
value="mac"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Linux
<input
id="linux"
value="linux"
name="platform"
type="radio"
onChange={this.handleChange}
/>
</div>
);
}
}
Link to example: https://codesandbox.io/s/6l6v9p0qkr
At first, none of the radio buttons is selected so this.state is an empty object, but whenever the radio button is selected this.state gets a new property with the name of the input and its value. It eases then to check whether user selected any radio-button like:
const isSelected = this.state.platform ? true : false;
EDIT:
With version 16.7-alpha of React there is a proposal for something called hooks which will let you do this kind of stuff easier:
In the example below there are two groups of radio-buttons in a functional component. Still, they have controlled inputs:
function App() {
const [platformValue, plaftormInputProps] = useRadioButtons("platform");
const [genderValue, genderInputProps] = useRadioButtons("gender");
return (
<div>
<form>
<fieldset>
Windows
<input
value="windows"
checked={platformValue === "windows"}
{...plaftormInputProps}
/>
Mac
<input
value="mac"
checked={platformValue === "mac"}
{...plaftormInputProps}
/>
Linux
<input
value="linux"
checked={platformValue === "linux"}
{...plaftormInputProps}
/>
</fieldset>
<fieldset>
Male
<input
value="male"
checked={genderValue === "male"}
{...genderInputProps}
/>
Female
<input
value="female"
checked={genderValue === "female"}
{...genderInputProps}
/>
</fieldset>
</form>
</div>
);
}
function useRadioButtons(name) {
const [value, setState] = useState(null);
const handleChange = e => {
setState(e.target.value);
};
const inputProps = {
name,
type: "radio",
onChange: handleChange
};
return [value, inputProps];
}
Working example: https://codesandbox.io/s/6l6v9p0qkr
Make the radio component as dumb component and pass props to from parent.
import React from "react";
const Radiocomponent = ({ value, setGender }) => (
<div onChange={setGender.bind(this)}>
<input type="radio" value="MALE" name="gender" defaultChecked={value ==="MALE"} /> Male
<input type="radio" value="FEMALE" name="gender" defaultChecked={value ==="FEMALE"}/> Female
</div>
);
export default Radiocomponent;
It's easy to test as it is a dumb component (a pure function).
Just an idea here: when it comes to radio inputs in React, I usually render all of them in a different way that was mentionned in the previous answers.
If this could help anyone who needs to render plenty of radio buttons:
import React from "react"
import ReactDOM from "react-dom"
// This Component should obviously be a class if you want it to work ;)
const RadioInputs = (props) => {
/*
[[Label, associated value], ...]
*/
const inputs = [["Male", "M"], ["Female", "F"], ["Other", "O"]]
return (
<div>
{
inputs.map(([text, value], i) => (
<div key={ i }>
<input type="radio"
checked={ this.state.gender === value }
onChange={ /* You'll need an event function here */ }
value={ value } />
{ text }
</div>
))
}
</div>
)
}
ReactDOM.render(
<RadioInputs />,
document.getElementById("root")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
import React, { Component } from "react";
class RadionButtons extends Component {
constructor(props) {
super(props);
this.state = {
// gender : "" , // use this one if you don't wanna any default value for gender
gender: "male" // we are using this state to store the value of the radio button and also use to display the active radio button
};
this.handleRadioChange = this.handleRadioChange.bind(this); // we require access to the state of component so we have to bind our function
}
// this function is called whenever you change the radion button
handleRadioChange(event) {
// set the new value of checked radion button to state using setState function which is async funtion
this.setState({
gender: event.target.value
});
}
render() {
return (
<div>
<div check>
<input
type="radio"
value="male" // this is te value which will be picked up after radio button change
checked={this.state.gender === "male"} // when this is true it show the male radio button in checked
onChange={this.handleRadioChange} // whenever it changes from checked to uncheck or via-versa it goes to the handleRadioChange function
/>
<span
style={{ marginLeft: "5px" }} // inline style in reactjs
>Male</span>
</div>
<div check>
<input
type="radio"
value="female"
checked={this.state.gender === "female"}
onChange={this.handleRadioChange}
/>
<span style={{ marginLeft: "5px" }}>Female</span>
</div>
</div>
);
}
}
export default RadionButtons;
Here's what I have used. Hope this helps.
Defining variable first.
const [variableName, setVariableName] = useState("");
Then, we will need the actual radio buttons.
<input
type="radio"
name="variableName"
value="variableToCheck"
onChange={(e) =>
setVariableName("variableToCheck")
}
checked={variableName === "variableToCheck"}
/>
#Tomasz Mularczyk mentions react hooks in his answer, but I thought I'd put in a solution I recently used that uses just the useState hook.
function Radio() {
const [currentRadioValue, setCurrentRadioValue] = useState()
const handleRadioChange = (e) => {
setCurrentValue(e.target.value);
};
return (
<>
<div>
<input
id="radio-item-1"
name="radio-item-1"
type="radio"
value="radio-1"
onChange={handleRadioChange}
checked={currentRadioValue === 'radio-1'}
/>
<label htmlFor="radio-item-1">Radio Item 1</label>
</div>
<div>
<input
id="radio-item-2"
name="radio-item-2"
type="radio"
value="radio-2"
onChange={handleRadioChange}
checked={currentRadioValue === 'radio-2'}
/>
<label htmlFor="radio-item-2">
Radio Item 1
</label>
</div>
</>
);
}
Clicking a radio button should trigger an event that either:
calls setState, if you only want the selection knowledge to be local, or
calls a callback that has been passed in from above self.props.selectionChanged(...)
In the first case, the change is state will trigger a re-render and you can do
<td>chosen site name {this.state.chosenSiteName} </td>
in the second case, the source of the callback will update things to ensure that down the line, your SearchResult instance will have chosenSiteName and chosenAddress set in it's props.
I also got confused in radio, checkbox implementation. What we need is, listen change event of the radio, and then set the state. I have made small example of gender selection.
/*
* A simple React component
*/
class App extends React.Component {
constructor(params) {
super(params)
// initial gender state set from props
this.state = {
gender: this.props.gender
}
this.setGender = this.setGender.bind(this)
}
setGender(e) {
this.setState({
gender: e.target.value
})
}
render() {
const {gender} = this.state
return <div>
Gender:
<div>
<input type="radio" checked={gender == "male"}
onClick={this.setGender} value="male" /> Male
<input type="radio" checked={gender == "female"}
onClick={this.setGender} value="female" /> Female
</div>
{ "Select Gender: " } {gender}
</div>;
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<App gender="male" />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
To build upon ChinKang said for his answer, I have a more dry'er approach and in es6 for those interested:
class RadioExample extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedRadio: 'public'
};
}
handleRadioChange = (event) => {
this.setState({
selectedRadio: event.currentTarget.value
})
};
render() {
return (
<div className="radio-row">
<div className="input-row">
<input
type="radio"
name="public"
value="public"
checked={this.state.selectedRadio === 'public'}
onChange={this.handleRadioChange}
/>
<label htmlFor="public">Public</label>
</div>
<div className="input-row">
<input
type="radio"
name="private"
value="private"
checked={this.state.selectedRadio === 'private'}
onChange={this.handleRadioChange}
/>
<label htmlFor="private">Private</label>
</div>
</div>
)
}
}
except this one would have a default checked value.
Bootstrap guys, we do it like this:
export default function RadioButton({ onChange, option }) {
const handleChange = event => {
onChange(event.target.value)
}
return (
<>
<div className="custom-control custom-radio">
<input
type="radio"
id={ option.option }
name="customRadio"
className="custom-control-input"
onChange={ handleChange }
value = { option.id }
/>
<label
className="custom-control-label"
htmlFor={ option.option }
>
{ option.option }
</label>
</div>
</>
)
}
import React from 'react';
import './style.css';
export default function App() {
const [currentRadioValue, setCurrentValue] = React.useState('on');
const handleRadioChange = value => {
setCurrentValue(value);
};
return (
<div>
<>
<div>
<input
name="radio-item-1"
value="on"
type="radio"
onChange={e => setCurrentValue(e.target.value)}
defaultChecked={currentRadioValue === 'on'}
/>
<label htmlFor="radio-item-1">Radio Item 1</label>
{currentRadioValue === 'on' && <div>one</div>}
</div>
<div>
<input
name="radio-item-1"
value="off"
type="radio"
onChange={e => setCurrentValue(e.target.value)}
defaultChecked={currentRadioValue === 'off'}
/>
<label htmlFor="radio-item-2">Radio Item 2</label>
{currentRadioValue === 'off' && <div>two</div>}
</div>
</>
</div>
);
}
working example: https://stackblitz.com/edit/react-ovnv2b
Related
I work on a React project. I wrote HTML codes for create form. There is validation in HTML scripts. I wrote validations but validations doesn't work. The code is below. For example I want the relevant field to be red when the name is not entered or I want it to give a warning message when the name does not comply with the text rules. I must do it without any library. How can I fix it ?
import React, { Component } from 'react'
import EmployeeService from '../services/EmployeeService';
class CreateEmployeeComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
firstName: '',
lastName: '',
emailId: ''
}
}
componentDidMount(){
if(this.state.id === '_add'){
return
}else{
EmployeeService.getEmployeeById(this.state.id).then( (res) =>{
let employee = res.data;
this.setState({firstName: employee.firstName,
lastName: employee.lastName,
emailId : employee.emailId
});
});
}
}
saveOrUpdateEmployee = (e) => {
e.preventDefault();
let employee = {firstName: this.state.firstName, lastName: this.state.lastName, emailId: this.state.emailId};
console.log('employee => ' + JSON.stringify(employee));
// step 5
if(this.state.id === '_add'){
EmployeeService.createEmployee(employee).then(res =>{
this.props.history.push('/employees');
});
}else{
EmployeeService.updateEmployee(employee, this.state.id).then( res => {
this.props.history.push('/employees');
});
}
}
changeFirstNameHandler= (event) => {
this.setState({firstName: event.target.value});
}
changeLastNameHandler= (event) => {
this.setState({lastName: event.target.value});
}
changeEmailHandler= (event) => {
this.setState({emailId: event.target.value});
}
cancel(){
this.props.history.push('/employees');
}
getTitle(){
if(this.state.id === '_add'){
return <h3 className="text-center">Add Employee</h3>
}else{
return <h3 className="text-center">Update Employee</h3>
}
}
onSubmit = e => {
e.preventDefault();
}
render() {
return (
<div>
<br></br>
<div className = "container">
<div className = "row">
<div className = "card col-md-6 offset-md-3 offset-md-3">
{
this.getTitle()
}
<div className = "card-body">
<form onSubmit={this.onSubmit} noValidate>
<div className = "form-group">
<label for="validationCustom01" class="form-label">First name</label>
<input type='text' maxLength={20} pattern='[A-Za-z]' placeholder="First Name" name="firstName" className="form-control"
value={this.state.firstName} onChange={this.changeFirstNameHandler} required/>
</div>
<div className = "form-group">
<label> Last Name: </label>
<input type='text' maxLength={20} pattern='[A-Za-z]'class="form-control" placeholder="Last Name" name="lastName" className="form-control"
value={this.state.lastName} onChange={this.changeLastNameHandler} required/>
</div>
<div className = "form-group">
<label> Email Id: </label>
<input type='email' maxLength={35} pattern='[A-Za-z]' placeholder="Email Address" name="emailId" className="form-control"
value={this.state.emailId} onChange={this.changeEmailHandler} required/>
</div>
<button type="submit" className="btn btn-success" onClick={this.saveOrUpdateEmployee}>Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style={{marginLeft: "10px"}}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CreateEmployeeComponent
Remove noValidate from your <form> element.
Additionally, I've personally found that Formik and Yup can be a helpful library.
(Sorry I missed that "no libraries" wasa constraint)
Edit:
I was a muppet and forgot to change the pattern
You can do one of 2 things:
Remove maxLength and change the pattern to pattern="[A-Za-z]{1,20}"
Where 20 will be the new max-length (so 20 or 35, depending on the field)
Only change the pattern to be pattern="[A-Za-z]+"
The + is needed to ensure 1 or more of the [A-Za-z] regex is done. https://regexr.com/
Also don't forget to remove noValidate from your <form> element.
This answer may also prove helpful in setting custom validity status messages and callbacks.
I am trying to add the bottom shadow to the search bar on scroll in react js. it is working well until I go on the second page of my app.
When I am trying to go on the second page, it showing
Uncaught TypeError: Cannot read property 'classList' of null
Not working code :
import React, { useEffect } from 'react';
import { AiOutlineSearch } from "react-icons/ai";
const SearchBar = ({ totalPrograms, programs, setPrograms }) => {
const handleScroll = () => {
if(window.scrollY) {
document.getElementById('sb-header').classList.add('h-shadow');
}
else {
document.getElementById('sb-header').classList.remove('h-shadow');
}
}
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {window.removeEventListener('scroll', handleScroll)};
},[]);
const handleSubmit = (event) => {
event.preventDefault();
const searchProgramName = document.getElementById('search').value;
if(searchProgramName) {
setPrograms(
programs.filter(program =>
program.name.toLowerCase().includes(searchProgramName)
)
);
}
else {
handleClick();
}
}
const handleClick = () => {
const allPhase = document.getElementsByName('phase');
const checkedPhaseValue = [];
allPhase.forEach(phase => {
if(phase.checked) {
checkedPhaseValue.push(phase.value);
}
});
setPrograms(checkedPhaseValue.length ?
totalPrograms.filter(
program => checkedPhaseValue.includes(program.phase.toLowerCase())
)
: totalPrograms
);
}
return (
<header id='sb-header' className="container header-sb">
<form className="container container-center" onSubmit={handleSubmit}>
<div className="type-search">
<AiOutlineSearch className="icon"/>
<input id="search" type="search" placeholder="search by program name"/>
</div>
<div className="checkbox-container">
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="open_application" value="application_open"/>
<label htmlFor="open_application">Open Application</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="application_in_review" value="application_review"/>
<label htmlFor="application_in_review">Application in Review</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="in_progress" value="in_progress"/>
<label htmlFor="in_progress">in Progress</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="completed" value="completed"/>
<label htmlFor="completed">Completed</label>
</div>
</div>
</form>
</header>
)
}
export default SearchBar;
On the second page, I am going by clicking on the Details button. Code for that :
import React from 'react';
import { Link } from 'react-router-dom';
import { FaAngleDoubleRight } from 'react-icons/fa';
import * as ROUTES from '../Constants/routes';
const ProgramCards = ({ program }) => {
return (
<div className="card">
<div className="card-header">
<h1 className="p-name">{program.name}</h1>
</div>
<div className="card-body">
<h3 className="p-category">{program.category}</h3>
<small className="p-phase">{(program.phase).replace('_', ' ')}</small>
<p className="p-description">{program.shortDescription}</p>
</div>
<div>
<div className="date-duration">
<small className="p-date">Start Date: {program.startDate}</small>
<small className="p-duration">Duration: {program.duration}</small>
</div>
<Link to={ROUTES.PROGRAM_DETAILS}>
<button className="card-button">
Details <FaAngleDoubleRight className="icon angled-icon" />
</button>
</Link>
</div>
</div>
)
}
export default ProgramCards;
My question is, why it is not working?
After this, I tried a different approach. And it is working well.
Working code :
import React, { useEffect, useState } from 'react';
import { AiOutlineSearch } from "react-icons/ai";
const SearchBar = ({ totalPrograms, programs, setPrograms }) => {
const [ scrolled, setScrolled ] = useState(false);
const handleScroll = () => {
if(window.scrollY > 10) {
setScrolled(true);
}
else {
setScrolled(false);
}
}
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => {window.removeEventListener('scroll', handleScroll)};
},[]);
const handleSubmit = (event) => {
event.preventDefault();
const searchProgramName = document.getElementById('search').value;
if(searchProgramName) {
setPrograms(
programs.filter(program =>
program.name.toLowerCase().includes(searchProgramName)
)
);
}
else {
handleClick();
}
}
const handleClick = () => {
const allPhase = document.getElementsByName('phase');
const checkedPhaseValue = [];
allPhase.forEach(phase => {
if(phase.checked) {
checkedPhaseValue.push(phase.value);
}
});
setPrograms(checkedPhaseValue.length ?
totalPrograms.filter(
program => checkedPhaseValue.includes(program.phase.toLowerCase())
)
: totalPrograms
);
}
return (
<header className={`container header-sb ${scrolled && 'h-shadow'}`}>
<form className="container container-center" onSubmit={handleSubmit}>
<div className="type-search">
<AiOutlineSearch className="icon"/>
<input id="search" type="search" placeholder="search by program name"/>
</div>
<div className="checkbox-container">
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="open_application" value="application_open"/>
<label htmlFor="open_application">Open Application</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="application_in_review" value="application_review"/>
<label htmlFor="application_in_review">Application in Review</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="in_progress" value="in_progress"/>
<label htmlFor="in_progress">in Progress</label>
</div>
<div className="d-inline">
<input type="checkbox" onClick={handleClick} name="phase" id="completed" value="completed"/>
<label htmlFor="completed">Completed</label>
</div>
</div>
</form>
</header>
)
}
export default SearchBar;
Your second approach is the better and the react way to do it. It is generally discouraged to query the DOM managed by react yourself. Use ref's if you need the DOM node.
The first example is not working because handleScroll will be recreated every time the component re-renders. Therefore removing the listener will not remove the original listener as the function referenced by handleScroll has changed.
Therefore when your component unmounts the listener will not be removed correctly but react will remove the DOM node. Next time you scroll the handler will still be called but the node you are trying to query isn't there anymore.
You have to create the listener inside of useEffect so that your removeEventListener references the correct function:
useEffect(() => {
const handleScroll = () => setScrolled(window.scrollY > 10);
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
},[]);
Alternatively you could use useRef to create a stable reference to you listener function.
This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 2 years ago.
i'am just a newbie at React , i'am trying to make a form , when submitting i call a function "handleSubmit" that saves all the information into the state, and then displaying it into a grid , but it wont work for some reasons .
i really do need your help : here is my code :
export default class products extends React.Component {
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.state =
{
QrCode: ''
}
}
handleSubmit(event) {
event.preventDefault();
const data = new FormData(event.target);
data.set('username', data.get('username').toUpperCase());
this.setState({QrCode: data});
console.log(this.state);
}
render() {
return (
<div >
<div >
<form onSubmit={this.handleSubmit}>
<label htmlFor="username">Enter username</label>
<input id="username" name="username" type="text" />
<button>Send data!</button>
</form>
</div>
<div style={{margin: '10%', marginTop : '5%'}}>
<GridComponent dataSource={this.state} />
</div>
</div>
);
}
}
and here what i get :
i keep getting empty array
and thank you.
In React, you don't need use form to take values from inputs.
export default class App extends React.Component {
constructor() {
super();
this.state = {
userName: ""
};
}
render() {
return (
<div className="App">
<label htmlFor="username">Enter username</label>
<input
value={this.state.userName}
onChange={e => this.setState({ userName: e.target.value })}
name="username"
type="text"
/>
<button
onClick={() => {
alert(this.state.userName);
}}
>
Send data!
</button>
</div>
);
}
}
Hope it help you!
I'm trying to change state by checking radio button. When I check it, it updates the value only after I check the next radio button. If I click first radio button it won't change the state, and if I check the second one it updates state with the previously checked radio button's value. Can anyone help me fixing this?
class App extends React.Component {
state = { checked: false, radioValue: '' }
handleChange = (event) => {
const target = event.target;
const value = target.value;
const name = target.name;
console.log("this.state", this.state); // Gets previous value
this.setState({
[name]: value
});
}
render() {
return (
<div className="wrapper">
<input
name="radioValue"
type="radio"
value="aaa"
checked={this.state.radioValue === 'aaa'}
onChange={this.handleChange} />First Radio Button
<br />
<input
name="radioValue"
type="radio"
value="bbb"
checked={this.state.radioValue === 'bbb'}
onChange={this.handleChange} />Second Radio Button
</div>
);
}
}
export default App;
this.setState({
[name]: value
},()=>console.log(this.state));
you can also check like this by using callback in setstate
Please try this.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: ""
};
}
handleChange = (event) => {
console.log("this.state", this.state); // Gets previous value
this.setState({
checked: event.target.value
});
}
render() {
return (
<div className="wrapper">
<input name="radioValue" type="radio" value="aaa"
checked={this.state.checked === 'aaa'}
onChange={this.handleChange} />
First Radio Button
<br />
<input name="radioValue" type="radio" value="bbb"
checked={this.state.checked === 'bbb'}
onChange={this.handleChange} />
Second Radio Button
</div>
);
}
}
It works well on my machine.
Folks, I think I'm either missing something here or I don't know what I don't know.
What I have is:
<form action="/orders/populate" method="post">
<input type="hidden" name="name" id="name"/>
<input type="hidden" name="rating" id="rating"/>
<input type="submit" name="commit" value="Rate Now" />
</form>
What I want to do is:
Class myComponent extends React.PureComponent {
handleSubmit(e) {
e.preventDefault(); // don't know if this is necessary
sendAnalytics();
// then form submit
}
render () {
return (
<form action="/orders/populate" method="post" onSubmit={this.handleSubmit.bind(this)}>
<input type="hidden" name="name" id="name"/>
<input type="hidden" name="rating" id="rating"/>
<input type="submit" name="commit" value="Rate Now" />
</form>
);
}
}
Don't know what has to be done here. Can someone point out an example similar to this? Or perhaps give me a sample code below?
All help appreciated.
Class myComponent extends React.PureComponent {
this.state = {
name: '' // initial value for name
rating: '' // initial value for rating
}
handleInput = e => {
this.setState({[e.target.name]: e.target.value})
}
handleSubmit = e => {
const { name, rating } = this.state;
e.preventDefault(); // yes, this is necessary otherwise it's refresh your page.
sendAnalytics(name, rating); // api call to store in DB. to call API use axios npm package
}
render () {
const { name, rating } = this.state;
return (
<form onSubmit={this.handleSubmit}>
<input type="text" name="name" value={name} id="name" onChange={(e) => this.handleSubmit(e)}/>
<input type="text" name="rating" value={rating} id="rating" onChange={(e) => this.handleSubmit(e)}/>
<input type="submit" name="commit" value="Rate Now" />
</form>
);
}
}
Have you looked at the docs for handling forms in React? This will give you insights in how to use forms with react, since it handles a bit different than regular html forms
This is a common problem I've faced in React. You have one of three ways:
1) Use a third party React-Form library to do the job. There are several.
2) Use React-hooks (a very recent addition to React).
3) Create a generic Form class to handle this state management for you...like so:
export default class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
values: {}
};
}
#boundMethod
handleSubmit(event) {
event.preventDefault();
this.props.submit(this.state.values);
}
#boundMethod
handleChange(event) {
const { name, value } = event.target;
const newValues = Object.assign(
{ ...this.state.values },
{ [name]: value }
);
this.setState({
values: newValues
});
}
public render() {
const { values } = this.state;
return (
<form onSubmit={this.handleSubmit} noValidate={true}>
<div>
{React.Children.map(
this.props.children,
child => (
{React.cloneElement(child, {
value: values[child.props.name],
onChange: this.handleChange
})}
)
)}
<div>
<button type="submit">
Submit
</button>
</div>
</div>
</form>
);
}
}
Then you will be able to use this Form class like so:
<Form
submit={values => {
/* work with values */
}}
>
<input type="hidden" name="name" />
<input type="hidden" name="rating" />
</Form>;
PS:
Keep in mind boundMethod Decorator is something that is not natively available but a module called 'autobind-decorator' I tend to use a lot to deal with this not being bound.