As the title says I have an error that says "cannot read property 'length' of undefined" when I'm handling change in FormInput
. However, if I refactor my SignUp
component from functional to class component everything magically works. Is there anything I do wrong in the functional component while using useState or what's the deal? I have attached a code of components that are involved in this issue. In details otherProps.value.length
inside FormInput
triggers an error upon handleChange
helper function call.
Functional component:
function SignUp() {
const [newUser, setNewUser] = useState({
displayName: "",
email: "",
password: "",
confirmPassword: "",
});
const handleSubmit = async (e) => {
const { displayName, email, password, confirmPassword } = newUser;
e.preventDefault();
if (password !== confirmPassword) {
alert("passwords don't match");
return;
}
try {
const { user } = await auth.createUserWithEmailAndPassword(
email,
password
);
await createUserProfileDocument(user, { displayName });
setNewUser({
displayName: "",
email: "",
password: "",
confirmPassword: "",
});
} catch (error) {
console.error(error);
}
};
const handleChange = (e) => {
const { value, name } = e.target;
setNewUser({ [name]: value });
};
return (
<div className="sign-up">
<h2 className="title">I do not have a account</h2>
<span>Sign up wit your email and password</span>
<form className="sign-up-form" onSubmit={handleSubmit}>
<FormInput
type="text"
name="displayName"
value={newUser.displayName}
handleChange={handleChange}
label="Display Name"
required
/>
<FormInput
type="email"
name="email"
value={newUser.email}
handleChange={handleChange}
label="Email"
required
/>
<FormInput
type="password"
name="password"
value={newUser.password}
handleChange={handleChange}
label="Password"
required
/>
<FormInput
type="password"
name="confirmPassword"
value={newUser.confirmPassword}
handleChange={handleChange}
label="Confirm Password"
required
/>
<CustomButton type="submit">Sign up</CustomButton>
</form>
</div>
);
}
Class component:
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
displayName: "",
email: "",
password: "",
confirmPassword: "",
};
}
handleSubmit = async (e) => {
const { displayName, email, password, confirmPassword } = this.state;
e.preventDefault();
if (password !== confirmPassword) {
alert("passwords don't match");
return;
}
try {
const { user } = await auth.createUserWithEmailAndPassword(
email,
password
);
await createUserProfileDocument(user, { displayName });
this.setState({
displayName: "",
email: "",
password: "",
confirmPassword: "",
});
} catch (error) {
console.error(error);
}
};
handleChange = (e) => {
const { value, name } = e.target;
this.setState({ [name]: value });
};
render() {
const { displayName, email, password, confirmPassword } = this.state;
return (
<div className="sign-up">
<h2 className="title">I do not have a account</h2>
<span>Sign up wit your email and password</span>
<form className="sign-up-form" onSubmit={this.handleSubmit}>
<FormInput
type="text"
name="displayName"
value={displayName}
handleChange={this.handleChange}
label="Display Name"
required
/>
<FormInput
type="email"
name="email"
value={email}
handleChange={this.handleChange}
label="Email"
required
/>
<FormInput
type="password"
name="password"
value={password}
handleChange={this.handleChange}
label="Password"
required
/>
<FormInput
type="password"
name="confirmPassword"
value={confirmPassword}
handleChange={this.handleChange}
label="Confirm Password"
required
/>
<CustomButton type="submit">Sign up</CustomButton>
</form>
</div>
);
}
}
FormInput:
function FormInput({ handleChange, label, ...otherProps }) {
return (
<div className="group">
<input className="form-input" onChange={handleChange} {...otherProps} />
{label ? (
<label
className={`${otherProps.value.length ? "shrink" : ""
} form-input-label`}
>
{label}
</label>
) : null}
</div>
);
}