Search code examples
javascriptreactjsformsevent-handlingcomponents

How to pass a form (Radio Button) data from child to parent onChange in REACT


I want to pass the form data from the child component to the parent component I know it's easy to send the data using the event handler called onSubmit but I need this data to pass whenever the form gets change

So I tried to do it like this: PS - This isn't my original code this is the code I write for practice this situation

App.js [Parent Component]

import React from 'react';
import Form from './Form';

function App() {

  function getData (data) {
      console.log (`coming from the parent component ${data}`)
  }

  return (
    <div className="App">
      <Form onSubmit={getData}/>
    </div>
  );
}

export default App;

Form.js [child Component]

import React from 'react';

function Form(props) {
  const [name, setName] = React.useState('');
  const [answer, setAnswer] = React.useState('');

  function handleChange(event) {
    setName(event.target.value);
    setAnswer(event.target.value);
    props.onSubmit(answer);
  }

  console.log(answer);

  return (
    <>
      <form>
        <input type="text" value={name} onChange={handleChange} />
        <br />
        <input
          onChange={handleChange}
          type="radio"
          value="Radio 1"
          name="paka"
        />{'Radio 1'}
        <input
          onChange={handleChange}
          type="radio"
          value="Radio 2"
          name="paka"
        />{'Radio 2'}
        
        <input
          onChange={handleChange}
          type="radio"
          value="Radio 3"
          name="paka"
        />{'Radio 3'}
        
        <input
          onChange={handleChange}
          type="radio"
          value="Radio 4"
          name="paka"
        />{'Radio 4'}
        
        <br />
      </form>
    </>
  );
}

export default Form;

so when I try to do it like this when I first press in radio child it's passes nothing. and when I press the another radio it's pass the previously pressed button

This following image will be a example


Solution

  • The reason why it's one step behind is that you're using trying to use the state you just set as the handler argument but since the operation to update the state is batched and asynchronous that state isn't immediately available. The easiest way to solve the problem outlined in this code is to send the event value as the handler argument: props.onSubmit(event.value).

    The alternative would be to add a useEffect hook to watch for changes in the answer state and then call the submit handler:

    useEffect(() => props.onSubmit(answer), [answer]);
    

    A couple of further points which my updated working example incorporates.

    1. Since you have just input elements you don't necessarily need a form element to wrap them.

    2. For better accessibility use labels for your inputs. Identify which label corresponds with what input by adding an htmlFor attribute on the label, and an id attribute on the input.

    3. It would be easier (and probably more meaningful) to call your handler handleSubmit to differentiate it from the element event handler called onSubmit.

    const { Fragment, useState } = React;
    
    function App() {
    
      function handleSubmit(data) {
          console.log(`coming from the parent component ${data}`)
      }
    
      return (
        <div className="App">
          <Form handleSubmit={handleSubmit} />
        </div>
      );
    }
    
    function Form({ handleSubmit }) {
      
      const [name, setName] = useState('');
      const [answer, setAnswer] = useState('');
    
      function handleChange(event) {
        const { value } = event.target;
        setName(value);
        setAnswer(value);
        handleSubmit(value);
      }
    
      return (
        <Fragment>
          <section className="name">
            <label htmlFor="name">Name</label>
            <input
              id="name"
              type="text"
              value={name}
              onChange={handleChange}
            />
          </section>
          <section className="radios">
            <label htmlFor="radio1">Radio 1</label>
            <input
              id="radio1"
              onChange={handleChange}
              type="radio"
              value="Radio 1"
              name="paka"
            />
            <label htmlFor="radio2">Radio 2</label>
            <input
              id="radio2"
              onChange={handleChange}
              type="radio"
              value="Radio 2"
              name="paka"
            />
            <label htmlFor="radio3">Radio 3</label>
            <input
              id="radio3"
              onChange={handleChange}
              type="radio"
              value="Radio 3"
              name="paka"
            />
            <label htmlFor="radio4">Radio 4</label>
            <input
              id="radio4"
              onChange={handleChange}
              type="radio"
              value="Radio 4"
              name="paka"
            />
          </section>
        </Fragment>
      );
    
    }
    
    
    const node = document.getElementById('root');
    const root = ReactDOM.createRoot(node);
    root.render(<App />);
    .name label { margin-right: 0.25em; }
    .radios label:not(:first-child) { margin-left: 0.5rem; } 
    .radios { margin-top: 1em; }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
    <div id="root"></div>