how to prevent useEffect to display validation erros when the page loading? - reactjs

const Login = () => {
const [errors, setErrors] = useState({})
var newErrors = {}
const formValidation = () => {
if (name === "") {
newErrors.name = Name Can't Be Blanck
}
if (email === "") {
newErrors.email = <h1 className="text-red-800 text-center"> Email Address Is Required</h1>
} else if (/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) {
newErrors.email = <h1 className="text-red-800 text-center">Email address is invalid</h1>
} else {
newErrors.email = <h1 className="text-green-800 text-center ">Email is Valid</h1>
}
if (password === "") {
newErrors.password = <h1 className="text-red-800 text-center">Password Is Required</h1>
} else if (!/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,15}$/.test(password)) {
newErrors.password = <h1 className="text-red-800 text-center">Invalid Password Format</h1>
} else {
newErrors.password = <h1 className="text-green-800 text-center ">Correct Password</h1>
}
setErrors(newErrors)
}
const handleSubmit = (e) => {
e.preventDefault()
formValidation()
console.log({ name, email, password })
}
useEffect(() => {
if (newErrors) {
formValidation()enter code here
}
}, [name, email, password])
return (
<div
className="login grid place-content-center
bg-gradient-to-r from-purple-900 via-purple-1000 to-blue-800 "
>
<form
className="card grid place-content-center h-96 w-96
"
onSubmit={handleSubmit}
>
<label htmlFor="">name:</label>
<input
type="text"
value={name}
placeholder="Enter Your Name"
onChange={(e) => setName(e.target.value)}
/>
{errors.name}
</form>
</div>
)
}
export default Login

The way i'd do it is to add a state called loading const {loading,setLoading}= useState(false)
set loading to true when , of course , loading.
and render errors conditionally using { loading? null : errors.name}

Related

no change despite using useState

I have to make a dynamic survey where each next question comes from the previous one.
I want based on clicks to display/hide each question. I am doing this with style.display = "none" or "".
This the components' code.
As of now when I click the button, the isVisible array gets populated with next question's Id.
function refreshResults(questions, currentqId, val, isVisible) {
var nextqId = val.target.value;
// var currentqIdIdx = questions.findIndex(q => q.qID === currentqId)
var nextqIdIdx = questions.findIndex(q => q.qID === nextqId);
var ret = isVisible;
if (!isVisible.includes(nextqId)) {
ret.push(nextqId);
}
return ret;
}
const Questionnaire = () => {
const navigate = useNavigate();
let params = useParams();
let questionnaireId = params.questionnaireId;
// const [questionnaire, setQuestionnaire] = useState([]);
var questionnaire = test_input;
let questions = questionnaire.questions;
const [isVisible, setIsVisible] = useState([]);
useEffect(() => {
const setup = () => {
setIsVisible([questions[0].qID]);
// setIsVisible("");
}
setup();
}, [questions]);
return (
<div className="Questionnaire">
{
questions.forEach(question => {
if (isVisible.includes(question.qID)) {
question.style = { display: "" };
} else {
question.style = { display: "none" };
}
})
}
<>
{
questions.map((q) => (
<div className="card" style={q.style} key={q.qID}>
<div className="card-body" >
<h5 className="card-title">- {q.qtext}</h5>
<div className="buttons col">
{q.options.map(opt => (
<div key={opt.optID} className="form-check">
<input className="form-check-input" type="radio" name={`flexRadio${q.qID}`} value={opt.nextqID} onClick={clickValue => setIsVisible(refreshResults(questions, q.qID, clickValue, isVisible))} />
<label className="form-check-label" >
{opt.opttxt}
</label>
</div>
))
}
</div>
</div >
</div>
))
}
</>
<div className="submitButton"><input className="btn btn-primary" type="submit" value="Submit" /></div>
</div>
)
}```

How to show the users a message when they click submit button and the input field is empty on react?

I want to show an error message to users when they press a button with empty input fields (something like "YOU HAVE TO FILL ALL REQUIRED FIELDS"). This is my code:
Here is the Form component that has the input fields and submitt button and you can see the component has props:
export default function Form({ loading, onSubmit }) {
const [title, setTitle] = useState('')
const [slug, setSlug] = useState('')
const [preAmble, setPreAmble] = useState('')
const [slogan, setSlogan] = useState('')
const [box, setBox] = useState('')
const navigate = useNavigate()
// Function for å håndtere brukerinput
const inputValiderin = (title, slug, preAmble, slogan, box)
const handleSubmit = async (event) => {
event.preventDefault()
if (inputValiderin.length === 0) {
return
}
await onSubmit({ title, slug, preAmble, slogan, box })
navigate('/')
}
return (
<>
<h2 className="m-10 text-xl font-bold">Ny tjeneste</h2>
<form
className="ml-10 flex flex-col"
onSubmit={handleSubmit}
method="POST"
>
<label htmlFor="title" className="my-6 flex flex-col">
<span className="font-bold">Tittel*</span>
<input
className="w-1/2 rounded-lg bg-gray-100 "
onChange={(event) => setTitle(event.target.value)}
value={title}
data-testid="form_title"
type="text"
name="title"
id="title"
/>
</label>
<label htmlFor="slug" className="my-3 flex flex-col">
<span className="font-bold">Slug*</span>
<input
className="w-1/2 rounded-lg bg-gray-100 "
onChange={(event) => setSlug(event.target.value)}
value={slug}
data-testid="form_slug"
type="text"
name="slug"
id="slug"
/>
</label>
<button
className="float-left w-1/4 rounded-lg bg-green-700 py-2 font-medium text-white"
type="submit"
>
{loading ? 'Sender...' : 'Lag skjema'}
</button>
</form>
</>
)
}
and this is the main component that renders the changes
import { useState } from 'react'
import Form from '../components/Form'
import { sendForm } from '../lib/sendForm'
// Fil som lager "ny tjeneste" side og håndtåring av bruker input
export default function Create() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState(false)
const [success, setSuccess] = useState(false)
const onSubmit = async (data) => {
setLoading(true)
setError(false)
setSuccess(false)
try {
await sendForm(data)
setSuccess(true)
} catch (error) {
setError(error.message)
} finally {
setLoading(false)
}
}
return (
<>
<Form loading={loading} onSubmit={onSubmit} />
{success ? (
<p className="m-10 text-2xl font-semibold">Skjema er sendt</p>
) : null}
{error ? { error } : null}
</>
)
}
The way I do it is have a status state for each input, like this:
const [emailStatus, setStatusEmail] = useState(false)
Then, once the text is changed, all you need to do is have a function that handles the text change:
const changeHandler = (value) => {
if (value.length != 0) {
setStatusEmail(true)
}
}
This will check if the input isn't empty, of course you would need to then do your verifications to check if the input is actually valid. Once the user clicks the submit button, you need to check if the status is true, if it's false, there's a wrong or empty input:
if(emailStatus == false) {
Alert.alert(
"ERROR",
"Something went wrong... Please make sure you have inserted all data correctly.",
[{
text: "OK",
style: "ok",
}])
}

I am creating a frontend login form

The stack I am currently using is:
React, React-redux, styled-components, css3
I'm writing a natural responsive login form, and I'm trying to fix it with hooks while watching a course.
constructor(props) {
super(props);
this.state = {
isLogginActive: true
};
}
componentDidMount() {
//Add .right by default
this.rightSide.classList.add("right");
}
changeState() {
const { isLogginActive } = this.state;
if (isLogginActive) {
this.rightSide.classList.remove("right");
this.rightSide.classList.add("left");
} else {
this.rightSide.classList.remove("left");
this.rightSide.classList.add("right");
}
this.setState(prevState => ({ isLogginActive: !prevState.isLogginActive }));
}
render() {
const { isLogginActive } = this.state;
const current = isLogginActive ? "Register" : "Login";
const currentActive = isLogginActive ? "login" : "register";
return (
<div className="App">
<div className="login">
<div className="container" ref={ref => (this.container = ref)}>
{isLogginActive && (
<Login containerRef={ref => (this.current = ref)} />
)}
{!isLogginActive && (
<Register containerRef={ref => (this.current = ref)} />
)}
</div>
<RightSide
current={current}
currentActive={currentActive}
containerRef={ref => (this.rightSide = ref)}
onClick={this.changeState.bind(this)}
/>
</div>
</div>
);
}
}
const RightSide = props => {
return (
<div
className="right-side"
ref={props.containerRef}
onClick={props.onClick}
>
<div className="inner-container">
<div className="text">{props.current}</div>
</div>
</div>
);
};
export default App;
I'm working on the code from the lecture with hooks, but I'm starting to get confused about how to write a ref in the DOM.
export default function Modal() {
const dispatch = useDispatch();
const { isModal } = useSelector((state) => state.modal_Reducer);
const mainRef = useRef();
const rightRef = useRef();
const [isActive, setIsActive] = useState(true);
useEffect(() => {
rightRef.classList.add("right");
}, []);
const changeAuth = () => {
if (isActive) {
rightRef.classList.remove("right");
rightRef.classList.add("left");
} else {
rightRef.classList.remove("left");
rightRef.classList.add("rignt");
}
setIsActive(!isActive);
};
const onHideModal = () => {
dispatch(hideModal());
};
if (!isModal) {
return null;
}
const switchToSignup = isActive ? "Register" : "Login";
const switchToSignin = isActive ? "Login" : "Register";
return (
<ModalBackground>
<Main_Container>
<Auth_box ref={mainRef}>
{}
{}
</Auth_box>
<RightSide
ref={rightRef}
switchLogin={switchToSignin}
switcReg={switchToSignup}
onClick
/>
</Main_Container>
</ModalBackground>
It is a modal component that converts to the signup form while css animation effect occurs when the signup button is pressed in the login form.
I used useRef, but I think it's not right to use classList.add and .remove, so I need to fix it, but I'm not sure how to do it. Help.
useEffect(() => {
//rightRef.classList.add("right");
}, []);
const changeAuth = () => {
/* if (isActive) {
rightRef.classList.remove("right");
rightRef.classList.add("left");
} else {
rightRef.classList.remove("left");
rightRef.classList.add("rignt");
}
*/
setIsActive(!isActive);
};
and then
<RightSide
clsName={isActive? "right":"left"}
switchLogin={switchToSignin}
switcReg={switchToSignup}
onClick
/>
and update your component
const RightSide = props => {
return (
<div
className={`right-side ${props.clsName}`}
onClick={props.onClick}
>
<div className="inner-container">
<div className="text">{props.current}</div>
</div>
</div>
);
};
You can also explore https://www.npmjs.com/package/classnames npm package
useEffect(() => {
//rightRef.classList.add("right");
}, []);
const changeAuth = () => {
/* if (isActive) {
rightRef.classList.remove("right");
rightRef.classList.add("left");
} else {
rightRef.classList.remove("left");
rightRef.classList.add("rignt");
}
*/
setIsActive(!isActive);
};
and then
<RightSide
clsName={isActive? "right":"left"}
switchLogin={switchToSignin}
switcReg={switchToSignup}
onClick
/>
First, I set up a frame and kept working.
export default function Modal() {
const dispatch = useDispatch();
const { isModal } = useSelector((state) => state.modal_Reducer);
const mainRef = useRef();
const authRef = useRef();
const [isActive, setIsActive] = useState(true);
useEffect(() => {}, []);
const changeAuth = () => {
if (isActive) {
} else {
}
setIsActive(!isActive);
};
const onHideModal = () => {
dispatch(hideModal());
};
if (!isModal) {
return null;
}
const switchToSignup = isActive ? "Register" : "Login";
const switchToSignin = isActive ? "Login" : "Register";
return (
<ModalBackground>
<Main_Container ref={mainRef}>
<Auth_box>
{isActive && <Login authRef={authRef} />}
{!isActive && <Register authRef={authRef} />}
</Auth_box>
</Main_Container>
<RightSide
onSwitch={isActive ? "right" : "left"}
switchSignIn={switchToSignin}
switchRegister={switchToSignup}
onClick={changeAuth}
/>
</ModalBackground>
);
}
function RightSide(props) {
return (
<Right_Side
className={`right-side ${props.onSwitch}`}
ref={props.mainRef}
onClick={props.changeAuth}
>
<div className="inner-container">
<div className="text">{props.switchRegister}</div>
</div>
</Right_Side>
);
}
The problem is the login form conversion page, but the original uses ref and passes it as props.
login and register transfer current as ref.
But I don't understand why the container passes this.container.
original
export class Login extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="base-container" ref={this.props.containerRef}>
<div className="header">Login</div>
<div className="content">
<div className="image">
<img src={loginImg} />
</div>
<div className="form">
<div className="form-group">
<label htmlFor="username">Username</label>
<input type="text" name="username" placeholder="username" />
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input type="password" name="password" placeholder="password" />
</div>
</div>
</div>
<div className="footer">
<button type="button" className="btn">
Login
</button>
</div>
</div>
);
}
my code
function Login(props) {
const dispatch = useDispatch();
const history = useHistory();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [submited, setSubmited] = useState(false);
const [showPwd, setShowPwd] = useState(false);
const [alert, setAlert] = useState("");
const formInputValue = (key) => (e) => {
if (key === "EMAIL") setEmail(e.target.value);
if (key === "PASSWORD") setPassword(e.target.value);
console.log(e.target.value);
};
const onLogin = async (e) => {
e.preventDefault();
if (!email || !password) {
setAlert("이메일 주소와 비밀번호를 입력 하세요!");
}
let data = {
email: email,
password: password,
};
dispatch(loggedIn(data));
history.push("/");
};
return (
<PanelContainer ref={props.authRef}>
<h1>로그인</h1>
<PanelContent>
<Form onSubmit={onLogin}>
<InputGroup>
<label htmlFor="email">E-Mail</label>
<Input
type="text"
name="email"
value={email}
onChange={formInputValue("EMAIL")}
placeholder="E-mail"
/>
</InputGroup>
<InputGroup>
<label htmlFor="password">Password</label>
<Input
type={showPwd ? "text" : "password"}
name="password"
value={password}
onChange={formInputValue("PASSWORD")}
placeholder="Password"
/>
</InputGroup>
<SubmitBtn> 로그인 </SubmitBtn>
</Form>
</PanelContent>
</PanelContainer>
);
}
export default Login;

Testing with jest a register form ( react App)

It's my first time doing testing in general and I am trying to test a register page (check if the forms input are correctly working and if will create the user at the end). This is my register page:
import React, { useState, useRef } from "react";
import Form from "react-validation/build/form";
import Input from "react-validation/build/input";
import CheckButton from "react-validation/build/button";
import { isEmail } from "validator";
import AuthService from "../services/auth.service";
const required = (value) => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const validEmail = (value) => {
if (!isEmail(value)) {
return (
<div className="alert alert-danger" role="alert">
This is not a valid email.
</div>
);
}
};
const vusername = (value) => {
if (value.length < 3 || value.length > 20) {
return (
<div className="alert alert-danger" role="alert">
The username must be between 3 and 20 characters.
</div>
);
}
};
const vpassword = (value) => {
if (value.length < 6 || value.length > 40) {
return (
<div className="alert alert-danger" role="alert">
The password must be between 6 and 40 characters.
</div>
);
}
};
const Register = (props) => {
const form = useRef();
const checkBtn = useRef();
const [username, setUsername] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [successful, setSuccessful] = useState(false);
const [message, setMessage] = useState("");
const onChangeUsername = (e) => {
const username = e.target.value;
setUsername(username);
};
const onChangeEmail = (e) => {
const email = e.target.value;
setEmail(email);
};
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const handleRegister = (e) => {
e.preventDefault();
setMessage("");
setSuccessful(false);
form.current.validateAll();
if (checkBtn.current.context._errors.length === 0) {
AuthService.register(username, email, password).then(
(response) => {
setMessage(response.data.message);
setSuccessful(true);
},
(error) => {
const resMessage =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
setMessage(resMessage);
setSuccessful(false);
}
);
}
};
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form onSubmit={handleRegister} ref={form}>
{!successful && (
<div>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
validations={[required, vusername]}
/>
</div>
<div className="form-group">
<label htmlFor="email">Email</label>
<Input
type="text"
className="form-control"
name="email"
value={email}
onChange={onChangeEmail}
validations={[required, validEmail]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
validations={[required, vpassword]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block">Sign Up</button>
</div>
</div>
)}
{message && (
<div className="form-group">
<div
className={ successful ? "alert alert-success" : "alert alert-danger" }
role="alert"
>
{message}
</div>
</div>
)}
<CheckButton style={{ display: "none" }} ref={checkBtn} />
</Form>
</div>
</div>
);
};
export default Register;
My auhService is this :
import axios from "axios";
const API_URL = "http://localhost:8080/api/auth/";
const register = (username, email, password) => {
return axios.post(API_URL + "signup", {
username,
email,
password,
});
};
const login = (username, password) => {
return axios
.post(API_URL + "signin", {
username,
password,
})
.then((response) => {
if (response.data.accessToken) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
});
};
const logout = () => {
localStorage.removeItem("user");
};
const getCurrentUser = () => {
return JSON.parse(localStorage.getItem("user"));
};
export default {
register,
login,
logout,
getCurrentUser,
};
And this is my App.test.js :
import { configure, shallow, mount} from "enzyme";
import App from "./App"
import React, {useState} from "react";
import Adapter from "enzyme-adapter-react-16";
import Enzyme from "enzyme";
import Register from "./components/Register";
configure({ adapter: new Adapter() });
it("renders without crashing", () => {
shallow(<App />);
});
describe("<Register />", () => {
let wrapper;
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, "useState")
useStateSpy.mockImplementation((init) => [init, setState]);
beforeEach(() => {
wrapper = Enzyme.mount(Enzyme.shallow(<Register />).get(0))
});
afterEach(() => {
jest.clearAllMocks();
});
describe("username input", () => {
it("Should capture title correctly onChange", () => {
const title = wrapper.find("input").at(0);
title.instance().value = "New user";
title.simulate("change");
expect(setState).toHaveBeenCalledWith("New user");
});
});
describe("email input", () => {
it("Should capture content correctly onChange", () => {
const content = wrapper.find("new Email").at(1);
content.instance().value = "Testing";
content.simulate("change");
expect(setState).toHaveBeenCalledWith("new Email");
});
})
});
By far I was trying the first two inputs(username and email), but I'm getting an error: "TypeError: Cannot read property 'child' of undefined". What shall I do here? I've been trying everyhting. Thank you guys!

Creating a clear function for budgeting app

I am working on creating a clear button that once clicked will clear all the transactions that have been added to the transaction list with localStorage. My button works but its buggy, once it gets clicked I get the following error about a separate function I have to get the balance. If I refresh the page afterwords though all the transactions will be cleared.
The error I am receiving ...
TypeError: amounts.reduce(...).toFixed is not a function
my component
import react, {useState, useEffect} from 'react'
import Transaction from './Transaction'
const Form = () => {
//initial state
const [transaction, setTransaction] = useState({
description: '',
amount: ''
})
const [list, setList] = useState(
JSON.parse(localStorage.getItem('list')) || []
)
const [balance, setBalance] = useState('')
const [income, setIncome] = useState(
JSON.parse(localStorage.getItem('income'))
)
const [expense, setExpense] = useState(JSON.parse(localStorage.getItem('expense')))
//updates based onChange value
const updateBalance = (e) => {
setTransaction({
...transaction,
[e.target.name]:
e.target.type == 'number' ? parseInt(e.target.value) : e.target.value
})
}
//identify if transaction is income/expense
const plusMinus = () => {
transaction.amount > 0
? setIncome(income + transaction.amount)
: setExpense(expense + transaction.amount)
}
// updates balance after transaction is added
const getBalance = () => {
const amounts = list.map(i => i.amount);
const money = amounts.reduce((acc, item) => (acc += item), 0).toFixed(2);
setBalance(money)
}
useEffect(() => {
getBalance()
localStorage.setItem('list', JSON.stringify(list))
localStorage.setItem('income', JSON.stringify(income))
localStorage.setItem('expense', JSON.stringify(expense))
}, [list])
//clear transaction list
const clearBudget = () => {
localStorage.clear();
}
const onSubmit = e => {
e.preventDefault();
setList([transaction, ...list])
plusMinus()
setTransaction({ description: '', amount: ''})
}
return (
<div>
<div className='totals'>
<h2 className='balance'> Current Balance </h2>
<h3> ${balance} </h3>
<h4> Income: ${income} Expense: ${expense} </h4>
</div>
< br />
< br />
< br />
<h2 className='trans-history'> Transaction History </h2>
{list.map(i => {
return (
<div className='trans'>
<ul key={i.description}>
{i.description} ${parseInt(i.amount)}
</ul>
</div>
)
})}
<br />
<br />
<h2 className='enter-item'> Enter an Item </h2>
<form onSubmit={onSubmit}>
<div>
<input
type='text'
className="input-trans"
placeholder='Enter Transaction'
value={Transaction.description}
name='description'
onChange={updateBalance}
>
</input>
</div>
<div>
<input
type='number'
className='input-trans'
placeholder='Enter Amount'
name='amount'
value={transaction.amount}
onChange={updateBalance}
>
</input>
</div>
<br/>
<div className='button-container'>
<button type='submit' className='button is-primary'> Submit </button>
<button className='button is-danger' onClick={clearBudget}> Clear </button>
</div>
</form>
</div>
)
}
export default Form
Looks like amounts.reduce is returning something that is not a number. You could check the type before to perform toFixed function.
E.g.:
const amounts = list.map((i) => i.amount).map(Number);
const money = amounts.reduce((acc, item) => (acc += item), 0)
if (typeof money === 'number') {
setBalance(money.toFixed(2))
} else {
setBalance(money)
}

Resources