Home > Back-end >  React hooks - update state from an input in a child component which is called on a button click
React hooks - update state from an input in a child component which is called on a button click

Time:04-10

I’m trying to update a state from an input, which works fine when the field is is in the same component, but doesn’t work when I pass it into a child component, seemingly no matter what I pass to it!

const Create = () => {
      const [question, setQuestion] = useState(''); 
      const [components, setComponents] = useState([""]); 

        const handleQInputChange = event => {
              setQuestion(event.target.value)
        }
        
        function addComponent() {
        setComponents([...components, "Question"]) 
}

return (

       <Button onClick={addComponent} text="Next question"/>
            <ol>
                    {components.map((item, i) => ( 
                        <li>
                            <CreateQuestion question={question} onChange= 
                            {handleQInputChange}/>
                        </li> 
                    ))} 
                
            </ol>
)}

and then CreateQuestion component looks like:

     const CreateQuestion = (props) => {

    function handleQInputChange( event ) {
    props.onChange(event.target.value)
    }


    return (
      
        
         <div className="Component">
                <label>Question</label>
                <input
                    name="question"
                    id="question"
                    value={props.value}
                    onChange={props.handleQInputChange}
                    type="text"
                    placeholder="Question"
                />
                <br />

I've followed at least 10 guides on how to pass the data back and forth, so it may have become a little convoluted. If I put the Question input directly into the parent component the state updates, so I suspect it's just me not passing props correctly but completely stuck!

Thank you

CodePudding user response:

You are doing a lot of wrong things:

  • Using wrong props.
  • Passing event.target.value which is a string, to props.onChange which is a function that accepts a type Event.
  • Declaring the controlled input state on the parent, while you need the state local to the input, since you have multiple inputs and I don't think you want to share the same state among them.
function App() {
  const [questions, setQuestions] = useState([]);
  const [components, setComponents] = useState(['']);

  function addComponent() {
    setComponents([...components, 'Question']);
  }
  function addQuestion(question) {
    setQuestions((qs) => [...qs, question]);
  }

  return (
    <>
      <Button onClick={addComponent} text="Next question" />
      <ol>
        {components.map((item, i) => (
          <li>
            <CreateQuestion idx={i} addQuestion={addQuestion} />
          </li>
        ))}
      </ol>
      <ul>
        <h5>Submitted Questions:</h5>
        {questions.map((question, i) => (
          <li>
            <span style={{ marginRight: '10px' }}>
              Question n.{question.id}
            </span>
            <span>{question.body}</span>
          </li>
        ))}
      </ul>
    </>
  );
}

const CreateQuestion = ({ addQuestion, idx }) => {
  const [question, setQuestion] = useState('');

  const handleChange = (event) => {
    setQuestion(event.target.value);
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    addQuestion({ id: idx   1, body: question });
  };
  
  return (
    <div className="Component">
      <form onSubmit={handleSubmit}>
        <label></label>
        <input
          name="question"
          id="question"
          value={question}
          onChange={handleChange}
          type="text"
          placeholder="Question"
        />
        <button>ADD QUESTION</button>
      </form>
    </div>
  );
};

I refactored your code in this demo https://stackblitz.com/edit/react-h7m1cu check if it helps you.
I fixed the main issues, moved the input state down to the CreateQuestion Component, added a function and a state to the Parent Component that holds all the questions added when you submit the input, this way you can handle data in your Parent, if for example you want to send it to your server.

CodePudding user response:

Please change your CreateQuestion component like below

const CreateQuestion = (props) => {
    return (
         <div className="Component">
             <label>Question</label>
             <input
               name="question"
               id="question"
               value={props.value}
               onChange={props.onChange}
               type="text"
               placeholder="Question"
             />
         </div>
    );
 }

The problem is

You use an attribute like

onChange={props.handleQInputChange}

But you do not have handleQInputChange when you call your component as an attribute

<CreateQuestion question={question} onChange= 
                            {handleQInputChange}/>
  • Related