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.
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.
1class App extends Component {
2 state = {
3 textValue: "",
4 checkedOptionValue: "",
5 options: [],
6 };
7}
Create the text input to add the radio options along with an Add button.
1class App extends Component {
2 state = {
3 textValue: "",
4 checkedOptionValue: "",
5 options: [],
6 };
7
8 render() {
9 return (
10 <div className="App">
11 <input
12 type="text"
13 value={textValue}
14 onChange={(e) => this.setState({ textValue: e.target.value })}
15 />
16 <button onClick={this.handleOptionAdd}>Add</button>
17 </div>
18 );
19 }
20}
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.
1handleOptionAdd = () => {
2 const { options, textValue } = this.state;
3 if (textValue.trim().length === 0) return;
4 this.setState({ textValue: "" });
5 this.setState({
6 options: [
7 ...options,
8 {
9 label: textValue,
10 value: textValue.toLowerCase().replace(" ", "-"),
11 },
12 ],
13 });
14};
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.
1const { options, checkedOptionValue } = this.state;
2//
3{
4 options.map((option) => (
5 <div>
6 <input
7 type="radio"
8 name="dynamic-radio"
9 value={option.value}
10 checked={checkedOptionValue === option.value}
11 onChange={this.handleRadioChange}
12 />
13 <label>{option.label}</label>
14 </div>
15 ));
16}
Check out the entire code for the component in this section.
1import React, { Component } from "react";
2import "./styles.css";
3
4class App extends Component {
5 state = {
6 textValue: "",
7 checkedOptionValue: "",
8 options: [],
9 };
10
11 handleOptionAdd = () => {
12 const { options, textValue } = this.state;
13 this.setState({ textValue: "" });
14 this.setState({
15 options: [
16 ...options,
17 {
18 label: textValue,
19 value: textValue.toLowerCase().replace(" ", "-"),
20 },
21 ],
22 });
23 };
24
25 handleRadioChange = (e) =>
26 this.setState({ checkedOptionValue: e.target.value });
27
28 render() {
29 const { options, textValue, checkedOptionValue } = this.state;
30 return (
31 <div className="App">
32 <input
33 type="text"
34 value={textValue}
35 onChange={(e) => this.setState({ textValue: e.target.value })}
36 />
37 <button onClick={this.handleOptionAdd}>Add</button>
38
39 <div>
40 {options.map((option) => (
41 <div>
42 <input
43 type="radio"
44 name="dynamic-radio"
45 value={option.value}
46 checked={checkedOptionValue === option.value}
47 onChange={this.handleRadioChange}
48 />
49 <label>{option.label}</label>
50 </div>
51 ))}
52 </div>
53 </div>
54 );
55 }
56}
57
58export default App;
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.
1import React, { useState } from "react";
2import "./styles.css";
3
4function App() {
5 const [options, setOptions] = useState([]);
6
7 const [textValue, setTextValue] = useState("");
8
9 const [radioValue, setRadioValue] = useState("");
10
11 const handleOptionAdd = () => {
12 if (textValue.trim().length === 0) return;
13 setTextValue("");
14 setOptions([
15 ...options,
16 { label: textValue, value: textValue.toLowerCase().replace(" ", "-") },
17 ]);
18 };
19
20 return (
21 <div className="App">
22 <input
23 type="text"
24 value={textValue}
25 onChange={(e) => setTextValue(e.target.value)}
26 />
27 <button onClick={handleOptionAdd}>Add</button>
28
29 <div>
30 {options.map((option) => (
31 <div>
32 <input
33 type="radio"
34 name="dynamic-radio"
35 value={option.value}
36 checked={radioValue === option.value}
37 onChange={(e) => setRadioValue(e.target.value)}
38 />
39 <label>{option.label}</label>
40 </div>
41 ))}
42 </div>
43 </div>
44 );
45}
46
47export default App;
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.