Implementing a Radio List with Text Input in React
Jun 15, 2020 • 6 Minute Read
Introduction
Forms seem pretty easy to build in plain HTML, but when it comes to JavaScript frameworks like React, it can be a bit tricky. React expects you to manage the form data yourself using state, instead of relying on the DOM to maintain the form data.
When you have a fixed set of input fields, it is easy to add them in your component; but dynamic fields can be challenging as you need to maintain the fields in the state. In this guide, you will learn how to create a dynamic radio list using a text input.
Start With The Component Class
Since all the form data needs to be handled by state, create properties in state to store the text input and radio list value. Also, initialize an empty array in state to store the radio options.
class App extends Component {
state = {
textValue: "",
checkedOptionValue: "",
options: [],
};
}
Create the text input to add the radio options along with an Add button.
class App extends Component {
state = {
textValue: "",
checkedOptionValue: "",
options: [],
};
render() {
return (
<div className="App">
<input
type="text"
value={textValue}
onChange={(e) => this.setState({ textValue: e.target.value })}
/>
<button onClick={this.handleOptionAdd}>Add</button>
</div>
);
}
}
When the user clicks on the Add button, push the text value into the options state. Only push the text value if its length is greater than zero; otherwise, you will end up with radio options with empty values. Another thing to note here is that you need to store the option as an object of label and value.
handleOptionAdd = () => {
const { options, textValue } = this.state;
if (textValue.trim().length === 0) return;
this.setState({ textValue: "" });
this.setState({
options: [
...options,
{
label: textValue,
value: textValue.toLowerCase().replace(" ", "-"),
},
],
});
};
Rendering the Radio List
To render the radio list, use the map() method to iterate over the options state array. Ensure that all the radio inputs have the same name prop so that the user can only check one radio input.
const { options, checkedOptionValue } = this.state;
//
{
options.map((option) => (
<div>
<input
type="radio"
name="dynamic-radio"
value={option.value}
checked={checkedOptionValue === option.value}
onChange={this.handleRadioChange}
/>
<label>{option.label}</label>
</div>
));
}
Complete Component Code
Check out the entire code for the component in this section.
import React, { Component } from "react";
import "./styles.css";
class App extends Component {
state = {
textValue: "",
checkedOptionValue: "",
options: [],
};
handleOptionAdd = () => {
const { options, textValue } = this.state;
this.setState({ textValue: "" });
this.setState({
options: [
...options,
{
label: textValue,
value: textValue.toLowerCase().replace(" ", "-"),
},
],
});
};
handleRadioChange = (e) =>
this.setState({ checkedOptionValue: e.target.value });
render() {
const { options, textValue, checkedOptionValue } = this.state;
return (
<div className="App">
<input
type="text"
value={textValue}
onChange={(e) => this.setState({ textValue: e.target.value })}
/>
<button onClick={this.handleOptionAdd}>Add</button>
<div>
{options.map((option) => (
<div>
<input
type="radio"
name="dynamic-radio"
value={option.value}
checked={checkedOptionValue === option.value}
onChange={this.handleRadioChange}
/>
<label>{option.label}</label>
</div>
))}
</div>
</div>
);
}
}
export default App;
Implementation Using Hooks
You can implement the same thing using the useState hook in a functional component. Store the options, textValue, and radioValue in separate state variables, and the rest of the component works similarly to the class component.
import React, { useState } from "react";
import "./styles.css";
function App() {
const [options, setOptions] = useState([]);
const [textValue, setTextValue] = useState("");
const [radioValue, setRadioValue] = useState("");
const handleOptionAdd = () => {
if (textValue.trim().length === 0) return;
setTextValue("");
setOptions([
...options,
{ label: textValue, value: textValue.toLowerCase().replace(" ", "-") },
]);
};
return (
<div className="App">
<input
type="text"
value={textValue}
onChange={(e) => setTextValue(e.target.value)}
/>
<button onClick={handleOptionAdd}>Add</button>
<div>
{options.map((option) => (
<div>
<input
type="radio"
name="dynamic-radio"
value={option.value}
checked={radioValue === option.value}
onChange={(e) => setRadioValue(e.target.value)}
/>
<label>{option.label}</label>
</div>
))}
</div>
</div>
);
}
export default App;
Conclusion
It is vital when building forms in React that you think in terms of state and how to manage the form data using state. Relying on the DOM to handle the form data can lead to unpredictable results, making the code difficult to test. Handling the radio list in React is a bit different from what you usually do in HTML, but it gets easier to set up in React once you understand how to deal with values in the state.