I am trying to build a login form. I am trying to set up the value of the email & password field individually. But as soon as I try to enter the text in the email text field, the same appears in the password field too. Can I have a solution to this?
Below is the code.
I guess the error is in OnChange fn where I am assigning the same value e.target.value to both the {email, passwaord}.
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
const LoginPage = () => {
let navigate = useNavigate();
const [credentials, setCredentials] = useState({email:"",password:""});
const onChange = (e) => {
setCredentials({email: e.target.value ,password: e.target.value})
console.log(credentials.email, credentials.password)
}
const goToSignUp = () => {
navigate("/signup");
}
return (
<>
<div className="container my-5">
<div id="loginbody">
<div className="mt-3">
<h2 className="my-3 display-3">Login Here</h2>
<form className="login-form p-5">
<div className="mb-3">
<label for="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
className="form-control"
id="email"
name="email"
value={credentials.email}
aria-describedby="emailHelp"
onChange={onChange}
/>
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label for="exampleInputPassword1" className="form-label">
Password
</label>
<input
type="password"
className="form-control"
id="password"
name="password"
value={credentials.password}
onChange={onChange}
/>
</div>
<div className="d-grid gap-2 my-4 col-6 mx-auto">
<button type="submit" className="btn btn-success">
Submit
</button>
</div>
<hr />
<div className="mb-3 text-center">
<div id="emailHelp" className="form-text center my-3">
Didn't have an account ?
</div>
<div className="d-grid gap-2 my-3 col-6 mx-auto">
<button onClick={goToSignUp} className="btn btn-success ">
SignUp Here !
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</>
);
};
export default LoginPage;
You have identified the problem. You need to pass the key to change as well.
Here passing a callback to setState which provides the current state as a parameter, cloning the state object using spread syntax, and then updating the relevant property in the copied object using the passed key as a computed property name.
const LoginPage = () => {
const [credentials, setCredentials] = useState({email:"",password:""});
const onChange = (e, key) => {
setCredentials(prevCredentials => ({...prevCredentials, [key]: e.target.value}))
}
return (
<>
//...
<input
type="email"
className="form-control"
id="email"
name="email"
value={credentials.email}
aria-describedby="emailHelp"
onChange={(e) => onChange(e, 'email')}
/>
//...
<input
type="password"
className="form-control"
id="password"
name="password"
value={credentials.password}
onChange={(e) => onChange(e, 'password')}
/>
//...
</>
);
};
Note: Calling console.log() right after setting state will not log the updated state, the new state values won't be available until the next render cycle. see: useState set method not reflecting change immediately
Use the proper key to the respective fields
const onChange = (e) => {
setCredentials({ ...credentials, [e.target.name]: e.target.value})
console.log(credentials);
}
Related
I am making a react project and when I am trying to sign up through email and password using firebase authentication then this 👇your text error is showing
This is the error I am getting
This is the whole code:
import React,{useState} from 'react';
import { Link, useNavigate } from 'react-router-dom';
import Layout from '../components/layout/Layout';
import {BsFillEyeFill} from "react-icons/bs";
import { getAuth, createUserWithEmailAndPassword , updateProfile} from "firebase/auth";
import {db} from "../firebase.config"
const SignUp = () => {
// to hide password
const [showPassword, setShowPassword] = useState(false);
const [formData, setFormData] = useState({
email:"",
name: "",
password: "",
})
const {name, email, password} = formData;
const navigate = useNavigate();
const onChange = (e) => {
setFormData((prevState) => ({
...prevState,
[e.target.id]: e.target.value,
}));
};
This is my signUp function
const onSubmitHandler= async (e) =>{
e.preventDefault();
try{
const auth= getAuth()
const userCredential = await createUserWithEmailAndPassword(auth,email,password);
const user = userCredential.user
updateProfile(auth.currentUser,{displayName: name})
navigate("/")
alert("Signup Success")
}catch(error){
console.log(error);
}
}
return (
<Layout>
<div className="d-flex align-items-center justify-content-center w-100 mt-5 mb-5">
<form className='bg-light p-4 rounded-3 border border-dark border-3' onSubmit={onSubmitHandler}>
<h4 className='bg-dark p-2 mt-2 text-light rounded-2 text-center'>Sign Up</h4>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">Enter Name</label>
<input
type="text"
defaultValue={name}
id="name"
onChange={onChange}
className="form-control"
aria-describedby="nameHelp"
/>
</div>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">Email address</label>
<input
type="email"
defaultValue={email}
onChange={onchange}
className="form-control"
id="email"
aria-describedby="emailHelp"
/>
<div id="emailHelp" className="form-text">We'll never share your email with anyone else.</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">Password</label>
<input
type={showPassword? "text" : "password"}
defaultValue={password}
onChange={onchange}
className="form-control"
id="password"
/>
<span >Show Password <BsFillEyeFill
className='text-primary ms-2'
style={{cursor: "pointer"}}
onClick={()=> setShowPassword((prevState)=> !prevState)}
/></span>
</div>
<button
type="submit"
className="btn btn-primary">Sign Up</button>
<div>
<h6>Login with Google</h6>
<span>Already User?</span> <Link to="/signin">Login</Link>
</div>
</form>
</div>
</Layout>
)
}
export default SignUp
After going through firebase documentation I have tried various emails but still it is showing the same error.
This is the email I had been using for signUp.
and I tried different emails as well like: piyush#gmail.com, and my personal email too but still it is showing the same error
This is the error message for your error
auth/invalid-email:
The provided value for the email user property is invalid. It must be a string email address.
Check to make sure your onSubmitHandler is within scope of your formData state, because your email and password might be undefined.
Your code has 2 issues,
1: Check the spelling of onChange on email and password field.
2: Change defaultValue to value
Below is the working code:
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { BsFillEyeFill } from 'react-icons/bs';
import {
getAuth,
createUserWithEmailAndPassword,
updateProfile,
} from 'firebase/auth';
const Signup = () => {
const [showPassword, setShowPassword] = useState(false);
const [formData, setFormData] = useState({
email: '',
name: '',
password: '',
});
const { name, email, password } = formData;
const navigate = useNavigate();
const onChange = (e: any) => {
setFormData((prevState) => ({
...prevState,
[e.target.id]: e.target.value,
}));
};
const onSubmitHandler = async (e) => {
e.preventDefault();
console.log('email is ', email, password);
try {
const auth = getAuth();
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password,
);
const user = userCredential.user;
alert('Signup Success');
} catch (error) {
console.log(error);
}
};
return (
<div className="d-flex align-items-center justify-content-center w-100 mt-5 mb-5">
<form
className="bg-light p-4 rounded-3 border border-dark border-3"
onSubmit={onSubmitHandler}
>
<h4 className="bg-dark p-2 mt-2 text-light rounded-2 text-center">
Sign Up
</h4>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Enter Name
</label>
<input
type="text"
value={name}
id="name"
onChange={onChange}
className="form-control"
aria-describedby="nameHelp"
/>
</div>
<div className="mb-3">
<label htmlFor="exampleInputEmail1" className="form-label">
Email address
</label>
<input
type="email"
value={email}
onChange={onChange}
className="form-control"
id="email"
aria-describedby="emailHelp"
/>
<div id="emailHelp" className="form-text">
We'll never share your email with anyone else.
</div>
</div>
<div className="mb-3">
<label htmlFor="exampleInputPassword1" className="form-label">
Password
</label>
<input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={onChange}
className="form-control"
id="password"
/>
<span>
Show Password{' '}
<BsFillEyeFill
className="text-primary ms-2"
style={{ cursor: 'pointer' }}
onClick={() => setShowPassword((prevState) => !prevState)}
/>
</span>
</div>
<button type="submit" className="btn btn-primary">
Sign Up
</button>
<div>
<h6>Login with Google</h6>
<span>Already User?</span> <Link to="/signin">Login</Link>
</div>
</form>
</div>
);
};
export default Signup;
I'm creating a simple controlled component form in react. When I console log event.target.name on onChange event it logs fine but when I setState using computed property name of the object in javascript it gives me an error TypeError: Cannot read properties of null (reading 'name')
This never happened before and I've created many working projects where I created the form similar way. Could you please explain why all of sudden these issues occurring to me?
import "./Contact.css";
import React from "react";
const Contact = () => {
const [contactFormValues, setContactFormValues] = React.useState({
fName: "",
lName: "",
email: "",
phone: "",
message: "",
});
const handleChange = (event) => {
setContactFormValues((prevValues) => ({
...prevValues,
[event.target.name]: event.target.value, // Here, this line gives me error
}));
};
const handleSubmit = async (event) => {
event.preventDefault();
console.log(contactFormValues);
}
return (
<>
<div className="contact-form">
<div className="contact-container">
<div className="left-section">
<div className="title">
<h1>Write Us</h1>
</div>
<div className="row">
<div className="contact-sub-row">
<label className="contact-label">First Name</label>
<input
className="contact-input"
type="text"
name="fName"
onChange={handleChange}
value={contactFormValues.fName}
required
/>
</div>
<div className="contact-sub-row">
<label className="contact-label">Last Name</label>
<input
className="contact-input"
type="text"
name="lName"
onChange={handleChange}
// value={contactFormValues.lName}
required
/>
</div>
</div>
<div className="contact-sub-row">
<label className="contact-label">Email</label>
<input
className="contact-input is-success"
type="email"
name="email"
onChange={handleChange}
// value={contactFormValues.email}
required
/>
</div>
<div className="contact-sub-row">
<label className="contact-label">Phone</label>
<input
className="contact-input"
type="tel"
placeholder="1-123-456-7890"
maxlength="20"
name="phone"
onChange={handleChange}
value={contactFormValues.phone}
required
/>
</div>
</div>
<div className="right-section">
<div className="your-message">
<label className="contact-label">Message</label>
<textarea
className="contact-textarea"
name="message"
placeholder="Write text here..."
onChange={handleChange}
value={contactFormValues.message}
required
></textarea>
</div>
<div className="contact-btns">
<button className="submit-btn" onClick={handleSubmit}>SEND MESSAGE</button>
</div>
</div>
</div>
</div>
</>
);
};
export default Contact;
Wrap the inputs in a form tag element.
and try this:
const handleChange = (event) => {
const { name, value } = event.target;
setContactFormValues({ ...contactFormValues, [name]: value });
}
I've done this in our very last project.
I have react components:
WelcomePage.jsx
import { useState } from "react";
import SignUpPage from "./SignUpPage";
function WelcomePage() {
const [signUp, toSignUp] = useState(false);
function signUpClick() {
toSignUp(true);
}
return (
<div>
{signUp ? (
<SignUpPage isOpen={toSignUp} back={toSignUp} />
) : (
<div className="Welcome_page__container animate__animated animate__fadeIn">
<h1 className="Welcome_page__title">Welcome to Hotel Review </h1>
<h3 className="Welcome_page__subtitle">Sign in :</h3>
<div className="Welcome_page__wrapper">
<label className="Welcome_page__input-title" htmlFor="welcome_mail">
E-mail:
</label>
<input
value={inputEmail}
onInput={(e) => setInputEmail(e.target.value)}
className="Welcome_page__input"
id="welcome_mail"
type="email"
placeholder="Your e-mail..."
/>
<label className="Welcome_page__input-title" htmlFor="welcome_pass">
Password:
</label>
<input
value={inputPass}
onInput={(e) => setInputPass(e.target.value)}
className="Welcome_page__input"
id="welcome_pass"
type="password"
placeholder="Your password..."
/>
<button className="Welcome_page__btn" onClick={loginClick}>
Login
</button>
<button className="Welcome_page__btn" onClick={signUpClick}>
Sign Up
</button>
</div>
</div>
)}
</div>
);
}
export default WelcomePage;
SignUpPage.jsx
import { useState } from "react";
import { Hotels } from "./Hotels";
function SignUpPage(props) {
const { isOpen, back } = props;
const [isSignUp, setSignUp] = useState(false);
return (
<div>
{isSignUp ? (
<Hotels />
) : (
<div className="Welcome_page__container animate__animated animate__fadeIn">
<button onClick={back(false)}>Back...</button>
<h1 className="Welcome_page__title">Welcome to Hotel Review </h1>
<h3 className="Welcome_page__subtitle">Sign up :</h3>
<div className="Welcome_page__wrapper">
<label className="Welcome_page__input-title" htmlFor="welcome_mail">
E-mail:
</label>
<input
value={inputEmail}
onInput={(e) => setInputEmail(e.target.value)}
className="Welcome_page__input"
id="welcome_mail"
type="email"
placeholder="Your e-mail..."
/>
<label className="Welcome_page__input-title" htmlFor="welcome_pass">
Password:
</label>
<input
value={inputPass}
onInput={(e) => setInputPass(e.target.value)}
className="Welcome_page__input"
id="welcome_pass"
type="password"
placeholder="Your password..."
/>
<label
className="Welcome_page__input-title"
htmlFor="welcome_pass"
></label>
<input
value={inputPass2}
onInput={(e) => setInputPass2(e.target.value)}
className="Welcome_page__input"
id="welcome_pass_repeat"
type="password"
placeholder="Repeat password..."
/>
<button className="Welcome_page__btn_2">Sign Up</button>
</div>
</div>
)}
</div>
);
}
export default SignUpPage;
When I click on Back in the SignUpPage component I get an error -
It is necessary that after clicking on back there is a return to WelcomePage.jsx
Maybe I'm not using or passing useState in SignUpPage.jsx
I read about the error on the Internet, they say that you need to useEffect (), but I doubt that this is the problem ...
Here is the error:
<button onClick={back(false)}>Back...</button>
You are basically calling the back function immediately causing the WelcomePage component to update while rendering the SignUpPage component.
You should do this:
<button onClick={() => back(false)}>Back...</button>
By doing this you are going to execute the back function only when you click on the button
I'm using formik in a reactjs project, and I want to use Prompt from react-router to open a notification before a user leaves and loses changes made to their submission.
I'd expected something like this to work:
<Prompt
when={formik.dirty}
message="You have unsaved changes. Are you sure you want to leave?"
/>
My formik block looks like this:
const formik = useFormik({
initialValues: {
<values>
},
enableReinitialize: true,
validate,
onSubmit: values => {
<submit functional stuff>
}
});
And my form is something like this:
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
disabled={isDisabled}
/>
{formik.errors.name ? <div className="text-danger">{formik.errors.name}</div> : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
disabled={isDisabled}
/>
{formik.errors.subject ? <div className="text-danger">{formik.errors.subject}</div> : null}
</div>
</div>
</form>
but it appears that formik.dirty is either not defined or it's not seen as true (despite making changes to the form).
How would I properly use the dirty prop to trigger the Prompt?
I am not sure what kind of setup you have, but I created a PoC with routing which has two tabs (links) for navigation and I am using prompt on tab with formik form component.
import React from "react";
import { Prompt } from "react-router-dom";
import { useFormik } from "formik";
const MyForm = () => {
const formik = useFormik({
initialValues: {
name: "",
subject: ""
},
enableReinitialize: true,
onSubmit: (values) => {
alert(JSON.stringify(values, null, 2));
},
onChange: (e) => {
console.log(e);
}
});
return (
<div>
<Prompt
when={!!formik.dirty}
message={(location) =>
`Are you sure you want to go to ${location.pathname}`
}
/>
<form id="myForm" onSubmit={formik.handleSubmit}>
<div className="row">
<div className="form-group">
<label htmlFor="name">Name</label>
<input
id="name"
type="text"
onChange={formik.handleChange}
value={formik.values.name}
className="form-control"
placeholder="Enter name"
/>
{formik.errors.name ? (
<div className="text-danger">{formik.errors.name}</div>
) : null}
</div>
<div className="form-group">
<label htmlFor="subject">Subject</label>
<input
id="subject"
type="text"
onChange={formik.handleChange}
value={formik.values.subject}
className="form-control"
placeholder="Email subject"
/>
{formik.errors.subject ? (
<div className="text-danger">{formik.errors.subject}</div>
) : null}
</div>
{formik.dirty && <button tye="submit">Save</button>}
</div>
</form>
</div>
);
};
export default MyForm;
take a look the this codesandbox.
I have a simple form build from React. Upon submission, if the error comes back, I wish to render an extra div to show error on the form.
Right now I got it to work but I do not like the solution. My solution is based on the knowledge that the component will only re-render if the state changes inside the render function (In this case this.state.errorMessages). So I had to explicitly put the if condition inside the render function like so
renderError() {
var errArr = [];
for (var key in this.state.errorMessages) {
errArr = [...errArr, ...this.state.errorMessages[key]];
}
return (
<div className="alert alert-danger">
{errArr.map((err) => {
return <p>{err}</p>
})}
</div>
)
}
renderForm() {
return (
<form onSubmit={this.handleRegisterFormSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" className="form-control" name="name" placeholder="Name" value={this.state.firstName} onChange={this.handleFirstNameChange} required/>
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleLastNameChange} required/>
</div>
<div className="form-group">
<label>Email address</label>
<input type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={this.state.email} onChange={this.handleEmailChange} />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" className="form-control" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange}/>
</div>
<div className="form-group">
<label>Password Confirmation</label>
<input type="password" className="form-control" name="password_confirmation" placeholder="Password Confirmation" value={this.state.passwordConfirmation} onChange={this.handlePasswordConfirmationChange}/>
</div>
<div>
<button type="submit" className="btn btn-primary">Submit</button>
<button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>Cancel</button>
</div>
</form>
)
}
render () {
if (!this.state.errorMessages) {
return (
<div>
{this.renderForm()}
</div>
)
} else {
return (
<div>
{this.renderForm()}
{this.renderError()}
</div>
)
}
}
I don't really like this approach as this could get nasty if I have more condition to re-render. I'm hoping there is a solution along the line of not having much logic in the actual render function and have that extracted out. For example...
renderError() {
if (!this.state.errorMessages) {
return;
}
var errArr = [];
for (var key in this.state.errorMessages) {
errArr = [...errArr, ...this.state.errorMessages[key]];
}
return (
<div className="alert alert-danger">
{errArr.map((err) => {
return <p>{err}</p>
})}
</div>
)
}
render () {
<form onSubmit={this.handleRegisterFormSubmit}>
<div className="form-group">
<label>First Name</label>
<input type="text" className="form-control" name="name" placeholder="Name" value={this.state.firstName} onChange={this.handleFirstNameChange} required/>
</div>
<div className="form-group">
<label>Last Name</label>
<input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleLastNameChange} required/>
</div>
<div className="form-group">
<label>Email address</label>
<input type="email" className="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email" value={this.state.email} onChange={this.handleEmailChange} />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" className="form-control" name="password" placeholder="Password" value={this.state.password} onChange={this.handlePasswordChange}/>
</div>
<div className="form-group">
<label>Password Confirmation</label>
<input type="password" className="form-control" name="password_confirmation" placeholder="Password Confirmation" value={this.state.passwordConfirmation} onChange={this.handlePasswordConfirmationChange}/>
</div>
<div>
<button type="submit" className="btn btn-primary">Submit</button>
<button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>Cancel</button>
</div>
{this.renderError}
</form>
}
This throws an error because it complains that this.renderError should be called as a function. But when I put it as
this.renderError()
The error will never render because it does not get automatically called when errors come back.
-----------Update----------
Alternatively, why can't I do something like the following
render () {
<div>
<form onSubmit={this.handleRegisterFormSubmit}>
...
</form>
{if (this.state.errorMessages) {this.renderError()}}
</div>
}
This throws console error
Uncaught Error: Module build failed: SyntaxError: Unexpected token
(117:13)
----------Update 2-----------
Essentially, I'm looking for a solution where inside the render function, I can easily show a whole block of code when the state changes. In Vue, I can do something like
<form>
<input type="text">
</form>
<div v-if="hasError">
<div class="alert alert-danger">{{something}}</div>
</div>
Can I do something as easy as this in React?
You can just use map in order to extract the error message from your object.
Here below a minimal example of form validations and errors in React. It's good to understand how it works, but for my part, I use Formik which simplifies this process.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = { errorMessages: {} };
}
handleRegisterFormSubmit = e => {
e.preventDefault(); // don't submit the form until we run what's below
let errorMessages = {};
if (!this.state.lastName) errorMessages.lastName = 'You need to enter your last name';
// And so on for each field validation
// Do we have errors ?
if (Object.keys(errorMessages).length > 0) {
this.setState(errorMessages);
} else {
// Submit to server
}
};
handleChange = e => {
this.setState({
[e.target.name]: e.target.value,
errorMessages: {
...this.state.errorMessages,
[e.target.name]: null // remove the error of this field if it's being edited
}
});
};
render() {
const errArr = Object.keys(this.state.errorMessages).map(key => this.state.errorMessages[key]);
return (
<form onSubmit={this.handleRegisterFormSubmit}>
<div className="form-group">
<label>Last Name</label>
<input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleChange} />
</div>
{/* ... your dom */}
<div>
<button type="submit" className="btn btn-primary">
Submit
</button>
<button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>
Cancel
</button>
</div>
{errArr.length > 0 && (
<div className="alert alert-danger">
{errArr.map(err => {
return <p>{err}</p>;
})}
</div>
)}
</form>
);
}
}
One more way to not display your alert div is with a ternary operator for your className and using bootstrap's d-none
<div className={errArr.length ? "alert alert-danger" : "d-none"}>
{errArr.map(err => {
return <p>{err}</p>;
})}
</div>
I believe this is an architectural question.
Try to follow those practices:
1- Inject conditional statement inside JSX directly
2- Use functional components to render JSX not object methods
1- Inject conditional statement inside JSX directly
Bad:
if (!this.state.errorMessages) {
return (
<div>
{this.renderForm()}
</div>
)
} else {
return (
<div>
{this.renderForm()}
{this.renderError()}
</div>
)
}
Good:
return <div>
{this.renderForm()}
{this.state.errorMessages && this.renderError()}
</div>
2- Use functional components to render JSX not object methods
Bad:
class FormComponent {
// ....
renderError() {
// Blah blah blah
return (
<div className="alert alert-danger">
{errArr.map((err) => {
return <p>{err}</p>
})}
</div>
)
}
render() {
return (
<div>
<AnotherComponent />
{this.renderError()}
<div/>
)
}
}
then {this.renderError()}
Good
class FormComponent {
// ....
render() {
return (
<div>
<AnotherComponent />
{<Error />} {/* render it as componet ⚠️*/}
<div/>
)
}
}
// Build it as component outside the main component (FormComponent) ⚠️
function Error(props) {
return (
<div className="alert alert-danger">
{props.errArr.map((err) => {
return <p>{err}</p>
})}
</div>
)
}
I spent many 2 years in React development of enterprise apps also graduating from Udacity and I am sharing my experience here. Congrats!
One would usually render items in an element representing the list with some styling applied. We also sometimes don't want this wrapping element if we don't have any items.
I've written the following component which allows conditional rendering. When the condition is false, no elements are rendered keeping the DOM clean.
export default class If extends Component<{ condition: any }, {}> {
render() {
return this.props.condition ? this.props.children : <Fragment></Fragment>;
}
}
One can now simply use the component as follow:
<If condition={items.length}>
<-- item list -->
</If>