Search code examples
reactjsstatereact-state-management

How to manage React state for reusable radio input?


I have a reusable radio button that I'm using throughout my application - a form. I want to store the form data in session storage so users can review their answers before submitting and because I will send the data to myself via EmailJS.
How should I manage state - should I define state in the radio button component or should I define state in each of the components where the radio button is used?

Reusable Radio Button Component

import styled from "styled-components"


function RadioButton({id, value, name, children}) {
  return (<>
    <SelectionContainer>
        <Input type='radio'  id={id} value={value}  name={name}/>
        <Label htmlForfor={id}>
            {children}
        </Label>
    </SelectionContainer>
  </>)
}

const Input = styled.input`
  margin-right: 1rem;
  height: 1.1rem;
  width: 1.1rem;
  accent-color: grey;
`;

const Label = styled.label`
  color: white;
`;


const SelectionContainer = styled.div`
  display: flex;
  align-items: center;
  justify-content: flex-start;
  width: 80%;
  height: 5rem;
`;

export default RadioButton

Example Component That Uses Radio Button

import React from 'react'
import styled from 'styled-components'
import QuestionText from '../utils/Question';
import RadioButton from '../utils/RadioButton';
import { useState } from 'react';


function BackgroundInfo() {
  
  return (<>
    <QuestionText>Do you have content ready for your site?</QuestionText>

    <RadioButton id='yes-content' value='Yes' name='user_contentReady'>
      Yes, I have content ready.
    </RadioButton>
  
    <RadioButton id='no-content' value='No' name='user_contentReady'>
      Not yet. I'm still working on it.
    </RadioButton>
  

    <QuestionText>Is your company legally registered?</QuestionText>

    <RadioButton id='yes-registered' value='Yes' name='user_companyRegistered' >
      Yes.
    </RadioButton>
     
    <RadioButton id='no-registered' value='No' name='user_companyRegistered'>
      No.
    </RadioButton>
   
    <RadioButton id='not-yet' value='Not yet' name='user_companyRegistered'>
      I'm in the process of registering it.
    </RadioButton>
    </>
  )
}

export default BackgroundInfo

Solution

  • You can use a custom hook to manage the state of the radio inputs and pass it as a prop to the reusable component. The custom hook can use the useState and useEffect hooks to store and update the selected value, and also provide a handleChange function to handle the input change event.

    For example:

    // RadioInput.js
    
    import React from "react";
    
    function RadioInput({ value, checked, label, ...rest }) {
      return (
        <label>
          <input value={value} checked={checked} {...rest} />
          {label}
        </label>
      );
    }
    
    export default RadioInput;
    
    
    // useRadio.js
    
    import { useState, useEffect } from "react";
    
    const useRadio = (name, defaultValue) => {
      const [value, setValue] = useState(defaultValue);
    
      useEffect(() => {
        const storedValue = sessionStorage.getItem(name);
        setValue(storedValue || defaultValue);
      }, [name, defaultValue]);
    
      const handleChange = (event) => {
        const newValue = event.target.value;
        setValue(newValue);
        sessionStorage.setItem(name, newValue);
      };
    
      const inputProps = {
        name,
        type: "radio",
        onChange: handleChange,
      };
    
      return [value, inputProps];
    };
    
    export default useRadio;
    
    
    // App.js
    
    import React from "react";
    import RadioInput from "./components/RadioInput";
    import useRadio from "./hooks/useRadio";
    
    const options = [
      { label: "Option 1", value: "option1" },
      { label: "Option 2", value: "option2" },
    ];
    
    function App() {
      const [value, inputProps] = useRadio("radio", "option1");
    
      return (
        <div>
          {options.map((option) => (
            <RadioInput
              key={option.value}
              value={option.value}
              checked={value === option.value}
              label={option.label}
              {...inputProps}
            />
          ))}
        </div>
      );
    }
    
    export default App;