React Radio Button Group Component
This challenge asks you to build a reusable React component that renders a group of radio buttons. Radio buttons are essential for allowing users to select one option from a predefined set, and a well-designed component simplifies their integration into various forms and interfaces. This exercise will reinforce your understanding of React state management, event handling, and component composition.
Problem Description
You need to create a RadioGroup component in React using TypeScript. This component should accept an array of options, each with a value and a label. The component should render a set of radio buttons, one for each option. The user should be able to select only one radio button at a time. The component should maintain an internal state to track the currently selected value and provide a way for the parent component to access this selected value.
Key Requirements:
- Props: The component should accept the following props:
options: An array of objects, where each object has avalue(string or number) and alabel(string). Example:[{ value: 'option1', label: 'Option 1' }, { value: 'option2', label: 'Option 2' }]value: (Optional) The initially selected value. If not provided, no radio button should be initially selected.onChange: A function that will be called when the selected radio button changes. This function should receive the new selected value as an argument.
- Rendering: The component should render a
<div>containing a set of<input type="radio">elements, one for each option. Each radio button should be associated with its corresponding option'svalue. - State Management: The component should manage its own internal state to track the currently selected value.
- Event Handling: The component should handle the
onChangeevent for each radio button and update its internal state accordingly. It should also call theonChangeprop function with the new selected value. - Accessibility: Ensure the radio buttons are accessible (e.g., proper labels, ARIA attributes if needed).
Expected Behavior:
- When the component mounts, if an initial
valueis provided, the corresponding radio button should be selected. - When a user clicks on a radio button, the component should update its internal state to reflect the new selection.
- The
onChangeprop function should be called with the new selected value whenever the user changes the selection. - Only one radio button should be selectable at a time.
Edge Cases to Consider:
- Empty
optionsarray: The component should render nothing or a suitable message. - Invalid
valueprop: Handle cases where the providedvaluedoesn't match any of the options. onChangeprop is not a function: Gracefully handle this situation (e.g., log a warning).
Examples
Example 1:
Input:
options: [{ value: 'a', label: 'Option A' }, { value: 'b', label: 'Option B' }]
value: 'a'
onChange: (selectedValue) => console.log('Selected:', selectedValue)
Output:
<div role="group" aria-label="Radio Group">
<input type="radio" id="a" name="group" value="a" checked onChange={() => onChange('a')} />
<label htmlFor="a">Option A</label>
<input type="radio" id="b" name="group" value="b" onChange={() => onChange('b')} />
<label htmlFor="b">Option B</label>
</div>
Explanation: The component renders two radio buttons, 'A' is initially selected because value is 'a'. Clicking 'B' will call onChange with 'b'.
Example 2:
Input:
options: [{ value: 'cat', label: 'Cat' }, { value: 'dog', label: 'Dog' }, { value: 'bird', label: 'Bird' }]
onChange: (selectedValue) => alert(selectedValue)
Output:
<div role="group" aria-label="Radio Group">
<input type="radio" id="cat" name="group" value="cat" onChange={() => onChange('cat')} />
<label htmlFor="cat">Cat</label>
<input type="radio" id="dog" name="group" value="dog" onChange={() => onChange('dog')} />
<label htmlFor="dog">Dog</label>
<input type="radio" id="bird" name="group" value="bird" onChange={() => onChange('bird')} />
<label htmlFor="bird">Bird</label>
</div>
Explanation: All radio buttons are initially unselected. Clicking any button will trigger the alert with the selected value.
Example 3:
Input:
options: []
value: 'someValue'
onChange: (selectedValue) => console.log(selectedValue)
Output:
(Nothing is rendered)
Explanation: Since the options array is empty, the component renders nothing. The value prop is ignored.
Constraints
- The component must be written in TypeScript.
- The component should be reusable and accept the
options,value, andonChangeprops. - The component should handle the edge case of an empty
optionsarray gracefully. - The component should be accessible.
- The component should be reasonably performant (no unnecessary re-renders).
Notes
- Consider using React's
useStatehook for state management. - Pay attention to accessibility best practices when rendering radio buttons and labels. Use
htmlForattributes to associate labels with their corresponding radio buttons. - The
nameattribute on the radio buttons should be the same for all buttons in the group. - Think about how to handle the case where the provided
valueprop doesn't match any of the options in theoptionsarray. You might choose to ignore it or log a warning.