0

Appreciate your help. I have an issue with the setValues in handleChange function, after the first onChange Event happens the form seems to reloaded and can't do more changes to the field. All I've been trying to do is to create a reusable form fields.

I have created a file BuildForm.js which has a custom hook that return the fields.

import { useState } from 'react';

const BuildForm = () => {
  const [values, setValues] = useState({});
  const [errors, setErrors] = useState({});

  const validate = (name, value) => {
    switch (name) {
      case 'first_name':
            if(value.trim() === ''){
              errors[name] = 'First Name is Rrequired.';
              setErrors(errors);
            }
            else{
              errors[name] = '';
              setErrors(errors);
            }
        break;
      case 'last_name':
            if(value.trim() === ''){ setErrors({...errors, [name]:'Last Name is Rrequired.'}); }
            else{
               setErrors({...errors, [name]:''});
             }
        break;
      default:
        break;
    }
  };
  const handleChange = (event) => {
      // event.persist();
      let name = event.target.name;
      let value = event.target.value;
      validate(name, value);
      setValues({...values, [name]:value});  // this is line with the issue
      console.log(name, value);
  };

  const Name = (props) => {
    return(
      <div className="fullwidth">
        <p className="description"><strong>{props.label}</strong></p>
        <div className="firstname halfwidth left">
          <label htmlFor="first_name">FIRST</label>
          <input type="text" name="first_name" onChange={handleChange}/>
          { errors.first_name && <p className="validation">{errors.first_name}</p> }
        </div>
        <div className="lastname halfwidth right">
          <label htmlFor="last_name">LAST</label>
          <input type="text" name="last_name" onChange={handleChange} />
          { errors.last_name && <p className="validation">{errors.last_name}</p> }
        </div>
      </div>
    );
  };

  return {
    Name,
  }
};

export default BuildForm;

and in the other file FirstForm.js where my form goes, i have this code

import './App.css';
import React, {useState} from 'react';
import { Link, Routes, Route, useNavigate } from 'react-router-dom';
import BuildForm from './Hooks/BuildForm';

function FirstForm() {
  const navigate = useNavigate();
  const MyForm = BuildForm();

  return (
    <div className="App">
      <div id="header">
        <img src={logo} className="logo" alt="logo"/>
      </div>
      <div id="pagebody">
        <div id="formcontainer">
          <form id="myForm">
            <MyForm.Name label="Client Name"/>
            <div className="submitbutton fullwidth"><input type="submit" /></div>
          </form>
        </div>
      </div>
    </div>
  );
}

export default FirstForm;

Martina
  • 1
  • 2

3 Answers3

0

You are missing the value attribute to inputs:

<input type="text" name="first_name" value={values.first_name} onChange={handleChange}/>
Abasaheb Gaware
  • 509
  • 4
  • 13
0

Try with this:

const handleChange = (event) => {
      // event.persist();
      let name = event.target.name;
      let value = event.target.value;
      values[name] = value;
      setValues(values);
      console.log(name, value);   };
  • But that cause another issue when i use this solution with the validate function to update the error that you can see it's rendered after the input tag. it doesn't allow the re-render. and as i found here https://stackoverflow.com/questions/56266575/why-is-usestate-not-triggering-re-render , i should use the 3-dots extend?! – Martina Nov 01 '22 at 17:48
0

That line is not the issue, nor do I see evidence of the hook you speak about. The issue is that you are treating a function as if it were a component.

Since values state does not affect the Name component i.e. the value is not being passed as a prop, react has no need to re-render Name component when values changes. Also since BuildForm is not being rendered, it gets no lifecycle updates, therefore its children also do not get re-rendered.

You need to rethink your design. Either move the state values into the Name component, or use an actual hook to create the state update logic.

Using a hook example

const BuildForm = () => {
  const useCreateValues = (initial) => { // <-- hook
    const [values, setValues] = useState(initial);
    const handleChange = (event) => {
      // event.persist();
      let name = event.target.name;
      let value = event.target.value;
      setValues(oldValues => ({...oldValues, [name]:value}));  // this is line with the issue
      console.log(name, value);
    };
    return [values, handleChange];
  };

  const Name = ({...props, initial = {}}) => {
    const [values, handleChange] = useCreateValues(initial);

    return(
      <div className="fullwidth">
        <p className="description"><strong>{props.label}</strong></p>
        <div className="firstname halfwidth left">
          <label htmlFor="first_name">FIRST</label>
          <input type="text" name="first_name" onChange={handleChange}/>
          { errors.first_name && <p className="validation">{errors.first_name}</p> }
        </div>
        <div className="lastname halfwidth right">
          <label htmlFor="last_name">LAST</label>
          <input type="text" name="last_name" onChange={handleChange} />
          { errors.last_name && <p className="validation">{errors.last_name}</p> }
        </div>
      </div>
    );
  };

  return {
    Name,
  }
};
smac89
  • 39,374
  • 15
  • 132
  • 179
  • Thanks @smac89 for replying, but my idea here is that I m gonna have multiple fields as i do for Name, like email, phone, address,..... to be reused in other forms. and i need to handle all the fields for every single form in one setValues and one setErrors. and when i used the hook as you set for every single field, it just define a new values and new errors for every field separately. – Martina Nov 01 '22 at 19:27
  • @Martina there are many ways of accomplishing this. One way is to use class components instead of functional components, and the extra components like `Name`, `Email`, etc, could be static properties of the class. I could post an answer, but there are already libraries out there that can accomplish this. One such library is `react-final-form`, and I recommend you use that instead of going down this path. – smac89 Nov 01 '22 at 20:18