In my React component
import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
import { NavLink } from "react-router-dom";
import "./styles/editIntern.sass";
const EditIntern = () => {
const { id } = useParams();
const [intern, setIntern] = useState([]);
const [name, inputName] = useState("");
const [email, inputEmail] = useState("");
const [start, inputStart] = useState("");
const [end, inputEnd] = useState("");
const [errorNameEmpty, isErrorNameEmpty] = useState(false);
const [errorEmailValid, iserrorEmailValid] = useState(false);
const [errorStartEmpty, isErrorStartEmpty] = useState(false);
const [errorEndEmpty, isErrorEndEmpty] = useState(false);
const validEmail = new RegExp(
/(\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)/gm
);
const onFormSubmit = (e) => {
e.preventDefault();
let startDate = new Date(start).getTime();
let endDate = new Date(end).getTime();
if (startDate > endDate) {
console.log("Start > end");
console.log(startDate);
console.log(endDate);
} else {
console.log("Ok");
console.log(startDate);
console.log(endDate);
}
};
useEffect(() => {
const fetchIntern = async () => {
const response = await fetch(`http://localhost:3001/interns/${id}`);
const intern = await response.json();
setIntern(intern);
};
fetchIntern();
console.log(`I want to get intern with id: ${id}!`);
}, [id]);
return (
<div className="container">
<img className="Logo" src="../logo.svg" alt="logo" />
<section className="EditIntern">
<NavLink to="/">
<button className="EditIntern_back">
<img
className="EditIntern_back-img"
src="../button_back_icon.svg"
alt="button_back"
/>{" "}
Back to list
</button>
</NavLink>
<form className="EditIntern_form">
<h4 className="EditIntern_form-title">Edit</h4>
<label className="EditIntern_form-label EditIntern_form-label_name">
Full name *
</label>
<input
className="EditIntern_form-input EditIntern_form-input_name"
type="text"
name="name"
value={name}
onChange={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
} else {
isErrorNameEmpty(false);
}
inputName(e.target.value);
}}
/>
{errorNameEmpty ? (
<span className="EditIntern_form-error EditIntern_form-error_name">
Name can't be empty
</span>
) : (
<></>
)}
<label className="EditIntern_form-label EditIntern_form-label_email">
Email address *
</label>
<input
className="EditIntern_form-input EditIntern_form-input_email"
type="text"
name="email"
value={email}
onChange={(e) => {
if (e.target.value === "") {
iserrorEmailValid(true);
} else if (!validEmail.test(e.target.value)) {
iserrorEmailValid(true);
} else {
iserrorEmailValid(false);
}
inputEmail(e.target.value);
}}
/>
{errorEmailValid ? (
<span className="EditIntern_form-error EditIntern_form-error_email">
Example: email#gmail.com
</span>
) : (
<></>
)}
<label className="EditIntern_form-label EditIntern_form-label_start">
Internship start *
</label>
<input
className="EditIntern_form-input EditIntern_form-input_start"
type="date"
name="email"
value={start}
onChange={(e) => {
if (!isNaN(e.target.valueAsNumber))
inputStart(e.target.valueAsNumber);
if (e.target.value === "") {
isErrorStartEmpty(true);
} else {
isErrorStartEmpty(false);
}
}}
/>
{errorStartEmpty ? (
<span className="EditIntern_form-error EditIntern_form-error_start">
Start date can't be empty
</span>
) : (
<></>
)}
<label className="EditIntern_form-label EditIntern_form-label_end">
Internship end *
</label>
<input
className="EditIntern_form-input EditIntern_form-input_end"
type="date"
name="email"
value={end}
onChange={(e) => {
if (!isNaN(e.target.valueAsNumber))
inputEnd(e.target.valueAsNumber);
if (e.target.value === "") {
isErrorEndEmpty(true);
} else {
isErrorEndEmpty(false);
}
}}
/>
{errorEndEmpty ? (
<span className="EditIntern_form-error EditIntern_form-error_end">
End date can't be empty
</span>
) : (
<></>
)}
<input
className="EditIntern_form-submit"
type="submit"
value="Submit"
onClick={onFormSubmit}
/>
</form>
</section>
</div>
);
};
export default EditIntern;
I need inputs to be filled with values from the intern array when this component is called (intern.name, intern.email ...) Now with useState the inputs are empty by default. I need by default with data from intern but with the ability to erase and fill in as you like.
As I already wrote, intern is an array with data that is rendered when this component is opened, it has all the data that needs to be placed by default.
The problem if i see correctly is in that you don't getting data from array intern you just getting it from name, email, etc.
So on start set that values like intern.name to name etc.
after that you could save it inside array using
setIntern({name: name, ...intern})
and contiue with that using other parametrs
Related
this code is working very well but if a person search a doctor using doctor name this time if any doctor name are not match with this input name this time I want to display a message like "Sorry, this name does not exist"
I'm trying but I can't solve the problem. Please give me some advice.
import React, {useState} from 'react';
import Doctordata from './Doctordata'
import { Link } from 'react-router-dom';
import './DoctorSection.css'
const DoctorSection = () => {
const [data] = useState(Doctordata);
const [query,setQuery] = useState("");
document.title = `Our Doctors`
return (
<>
<Link to="/"><button className="btnR"> <i className="fa-solid fa-arrow-left"></i> Back to Home</button></Link>
<div className='search'>
<input className='searchbox' placeholder="Search by doctor name..." type="text" onChange={e=>setQuery(e.target.value)} />
</div>
<div className='wrapper' id="doctor">
{data.filter((val)=>{
if(query === null){
return val
}
else if(val.d_name.toLowerCase().includes(query.toLowerCase())){
return val
}
else{
return false
}
})
.map((values) => {
const { id, src, d_name, d_info, primary } = values;
return (
<div className="doctor" key={id}>
<img className="doctor-img" src={src} alt={d_name}/>
<span className="d_name"><b>{d_name}</b></span>
<span className="d_info">{d_info}</span>
<div>
<div><button className="primary"
onClick={()=>{alert("Call us now on 000 0000 0000 or 111 1111 1111 for booking an appoimentment")}}>
{primary}</button> </div>
</div>
</div>
);
})}
</div>
</>
)
}
export default DoctorSection
Check the length of the filteredData. If it is 0 (and there is a query), then you can render the not found message.
import React, { useState } from "react";
import Doctordata from "./Doctordata";
const DoctorSection = () => {
const [data] = useState(Doctordata);
const [query, setQuery] = useState("");
const filteredData = data.filter(({ d_name }) => {
if (d_name.toLowerCase().includes(query.toLowerCase())) {
return true;
} else {
return false;
}
});
return (
<>
<input
placeholder="Search by doctor name..."
type="text"
onChange={(e) => setQuery(e.target.value)}
/>
{query && filteredData.length === 0 && (
<div>Sorry, this name does not exist</div>
)}
{filteredData.map(({ id, d_name }) => {
return <div key={id}>{d_name}</div>;
})}
</>
);
};
export default DoctorSection;
I'm reading from an API and displaying each student's information onto the web browser. My goal is to make each student have a text input field in which you are able to add a 'tag' for the specific student.
My problem: Currently, I have given each student a text input field but when I enter a 'tag' for ANY of the students, it only adds the tag to the FIRST student.
Home.jsx
import axios from 'axios';
import { useState, useEffect } from 'react';
import Students from '../components/Students';
import styles from "./Home.module.css";
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input className={styles.nameSearchInput} type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<Students key={student.id} student={student} />
))}
</div>
</>
)
}
export default Home;
Students.jsx
import { useState } from 'react';
import styles from "../views/Home.module.css";
import { v4 as uuidv4 } from 'uuid';
import AddIcon from '#mui/icons-material/Add';
import RemoveIcon from '#mui/icons-material/Remove';
const Students = ({student}) => {
const [isShown, setIsShown] = useState(true);
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const addTag = (event) => {
if (event.key === 'Enter') {
document.getElementById("tag-output").innerHTML += `
<p id="tag">${event.target.value}</p>
`;
document.getElementById("tag-input").value = "";
}
}
return (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}</p>
<button className={styles.expandBtn} onClick={() => {
setIsShown(!isShown);
}}>
{ isShown ? <AddIcon className={styles.expandBtn} /> : <RemoveIcon className={styles.expandBtn} /> }
</button>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
{!isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
<div id="tag-output"></div>
<input id="tag-input" className={styles.addTagInput} type="text" placeholder="Add a tag" onKeyPress={(e) => addTag(e)}/>
</ul>
</div>
</div>
)
}
export default Students;
use array state and append it with new values like this
play live code here live_demo
import React, { useState } from "react";
export default function Main(props) {
const [tags, setTags] = useState([]);
return (
<div className="App">
<input
type="text"
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey && e.target.value !== "") {
let val = e.target.value;
e.target.value = "";
setTags([...tags, val]);
}
}}
/>
<div className="tags">
<ol className="each-tag">
{tags.map((tag, i) => {
return <li key={i}>{tag}</li>;
})}
</ol>
</div>
</div>
);
}
There is a component:
import React, { useEffect, useState } from "react";
import { useParams } from "react-router";
import { NavLink } from "react-router-dom";
const EditIntern = () => {
const { id } = useParams();
const [intern, setIntern] = useState([]);
const [name, inputName] = useState("");
const [email, inputEmail] = useState("");
const [start, inputStart] = useState("");
const [end, inputEnd] = useState("");
const [errorNameEmpty, isErrorNameEmpty] = useState(true);
const [errorEmailEmpty, isErrorEmailEmpty] = useState(true);
const [errorEmailValid, iserrorEmailValid] = useState(false);
const [errorStartEmpty, isErrorStartEmpty] = useState(true);
const [errorEndEmpty, isErrorEndEmpty] = useState(true);
const validEmail = new RegExp(
/(\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)/gm
);
const dateValidate = () => {
if (start.value > end.value) {
console.log("Start > end");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
} else {
console.log("Ok");
console.log(start.valueAsNumber);
console.log(end.valueAsNumber);
}
};
useEffect(() => {
const fetchIntern = async () => {
const response = await fetch(`http://localhost:3001/interns/${id}`);
const intern = await response.json();
setIntern(intern);
};
fetchIntern();
console.log(`I want to get intern with id: ${id}!`);
}, [id]);
return (
<div>
<NavLink to="/">Back to list </NavLink>
<form>
<label>Name</label>
<input
type="text"
name="name"
value={name}
onChange={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
} else {
isErrorNameEmpty(false);
}
inputName(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorNameEmpty(true);
}
}}
/>
{errorNameEmpty ? <span>Name can't be empty</span> : <></>}
<label>Email</label>
<input
type="text"
name="email"
value={email}
onChange={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
} else if (!validEmail.test(e.target.value)) {
iserrorEmailValid(true);
isErrorEmailEmpty(false);
} else {
iserrorEmailValid(false);
isErrorEmailEmpty(false);
}
inputEmail(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEmailEmpty(true);
}
}}
/>
{errorEmailEmpty ? <span>Email can't be empty</span> : <></>}
{errorEmailValid ? <span>Example: email#gmail.com</span> : <></>}
<label>Start date</label>
<input
type="date"
name="email"
value={start}
onChange={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
} else {
isErrorStartEmpty(false);
}
inputStart(e.target.value);
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorStartEmpty(true);
}
}}
/>
{errorStartEmpty ? <span>Start date can't be empty</span> : <></>}
<label>End date</label>
<input
type="date"
name="email"
value={end}
onChange={(e) => {
inputEnd(e.target.value);
if (e.target.value === "") {
isErrorEndEmpty(true);
} else {
isErrorEndEmpty(false);
}
dateValidate();
}}
onClick={(e) => {
if (e.target.value === "") {
isErrorEndEmpty(true);
}
}}
/>
{errorEndEmpty ? <span>End date can't be empty</span> : <></>}
<input type="submit" value="Submit" />
</form>
<h2>{intern.name}</h2>
<h2>{intern.email}</h2>
<h2>{intern.internshipStart}</h2>
<h2>{intern.internshipEnd}</h2>
</div>
);
};
export default EditIntern;
It has two inputs of type date. The task is to check that the start date is not greater than the end date.
I created a dateValidate function for this (which doesn't work as it should).
How can you solve this problem? (Perhaps my approach to form validation is generally not correct, I’ll be happy to read about my mistakes)
transform start date and end date to milliseconds new Date(start date).getTime() than compare it (>, <, >=) to the end date also transformed into milliseconds
first select right property
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputStart(e.target.valueAsNumber);
// ...
//...
// inputEnd(e.target.value);
if (!isNaN(e.target.valueAsNumber)) inputEnd(e.target.valueAsNumber);
then validate
// ..when user submits start validation
const onFormSubmit = (e) => {
e.preventDefault()
// ...
if (start < end) {
// blah blah....
}
}
or better add min and max value so you dont have to check all
<input type="date" name="start" min="2017-01-01" max="2022-01-01">
also please dont use onChage with onClick event listeners its very bad practice
use either one of them, in this case onChage is pretty good.
seems use are using onClick for validating, use useEffect for validating and keep separate state for errors (object).
and check more options here MDN docs and play around with devtools (right click on element, select use in console option and see what properties it holds)
I have a react app (a sort of twitter clone) that uses firestore for storing posts and comments on posts. Each post is rendered as an element from an array using array.map(). Each post has a comment button that opens a form to take in a comment and add it to the post. When I enter a comment and submit it, the topmost post is always the one commented on no matter which post contained the comment button that was clicked(docId for the most recently saved firestore document is always submitted by the comment button instead of the docId corresponding to that instance of the component).
The map of the posts (called "howls"):
<div className="timeline">
{sortedHowls &&
sortedHowls.map((howl) => (
<Howl
key={howl.id}
image={howl.image}
text={howl.text}
time={howl.time}
userId={howl.userId}
docId={howl.id}
comments={howl.comments}
likes={howl.likes}
/>
))}
</div>
The Howl Component looks like this:
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { useFirestoreConnect } from "react-redux-firebase";
import { firestore } from "../../../firebase-store";
// styles
import "./Howl.scss";
// components
import Avatar from "../Avatar/Avatar";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
// functions
import timeCalc from "./timeCalc";
// icons
import { faStar, faComment } from "#fortawesome/free-solid-svg-icons";
const Howl = ({ docId, userId, text, image, time, comments, likes }) => {
useFirestoreConnect([{ collection: "users" }]);
const [commenting, toggleCommenting] = useState(false);
const [newComment, setNewComment] = useState("");
const [users, setUsers] = useState(null);
const [user, setUser] = useState(null);
const getUsers = useSelector((state) => state.firestore.ordered.users);
useEffect(() => {
if (!users) {
setUsers(getUsers);
} else {
setUser(users.find((doc) => doc.uid === userId));
}
}, [users, user, userId, getUsers]);
const handleLike = () => {
const newLikesTotal = likes + 1;
firestore.collection("howls").doc(docId).update({ likes: newLikesTotal });
};
const handleComment = () => {
toggleCommenting(!commenting);
};
const handleChange = (event) => {
setNewComment(event.currentTarget.value);
};
const submitComment = (event) => {
event.preventDefault();
const { id } = event.currentTarget;
console.log(event.currentTarget);
const resetComment = () => {
toggleCommenting(!commenting);
setNewComment("");
};
if (comments) {
firestore
.collection("howls")
.doc(id)
.update({
comments: [...comments, newComment],
})
.then(() => resetComment());
} else {
firestore
.collection("howls")
.doc(id)
.update({ comments: [newComment] })
.then(() => resetComment());
}
};
return (
<div className="howl">
<div className="avatar-container">
<Avatar
photoURL={user ? user.photoURL : ""}
displayName={user ? user.displayName : ""}
/>
</div>
<div className="name-text-img-container">
<p className="userName">
{user && user.displayName} - {timeCalc(Date.now(), time)}
</p>
<p className="howl-text">{text}</p>
<div className="img-container">
{image ? (
<img src={image} alt="user uploaded" className="img" />
) : null}
</div>
<div className="buttons-container">
<form action="" className="buttons">
<label htmlFor="comment-button">
<FontAwesomeIcon icon={faComment} className="image-icon" />
</label>
<input
id="comment-button"
type="checkbox"
onClick={handleComment}
style={{ display: "none" }}
/>
<label htmlFor="like-button">
<FontAwesomeIcon icon={faStar} className="image-icon" />
</label>
<input
id="like-button"
type="checkbox"
onClick={handleLike}
style={{ display: "none" }}
/>
<label htmlFor="like-button">{likes > 0 && likes}</label>
</form>
</div>
{commenting && (
<div className="comment-form">
<form action="submit" onSubmit={submitComment} id={docId}>
<input
type="text"
name="comment-input"
className="comment-input"
maxLength={128}
onChange={handleChange}
value={newComment}
placeholder="Enter comment"
/>
<div className="buttons">
<button type="submit">Submit</button>
<button onClick={() => toggleCommenting(!commenting)}>
Cancel
</button>
</div>
</form>
</div>
)}
<div className="comments">
{comments
? comments.map((comment, index) => {
return (
<p key={index} className="comment">
{comment}
</p>
);
})
: null}
</div>
</div>
</div>
);
};
export default Howl;
How can I get the comment button to specify the correct document to update?
Link to my full repo.
It turns out that the problem is here:
<form action="" className="buttons">
<label htmlFor="comment-button">
<FontAwesomeIcon icon={faComment} className="image-icon" />
</label>
<input
id="comment-button"
type="checkbox"
onClick={handleComment}
style={{ display: "none" }}
/>
<label htmlFor="like-button">
<FontAwesomeIcon icon={faStar} className="image-icon" />
</label>
<input
id="like-button"
type="checkbox"
onClick={handleLike}
style={{ display: "none" }}
/>
<label htmlFor="like-button">{likes > 0 && likes}</label>
</form>
By using a form and inputs as the buttons instead of using <button /> elements it somehow confused react as to which instance of <Howl /> was opening the comment form and therefore which docId was sent to submitComment. Corrected <Howl /> component:
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { useFirestoreConnect } from "react-redux-firebase";
import { firestore } from "../../../firebase-store";
// components
import Avatar from "../Avatar/Avatar";
import CommentInput from "./CommentInput";
import Comment from "./Comment";
import ViewProfile from "../ViewProfile/ViewProfile";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
// functions
import timeCalc from "./timeCalc";
// styles
import "./Howl.scss";
// icons
import { faStar, faComment } from "#fortawesome/free-solid-svg-icons";
const Howl = ({ howl }) => {
useFirestoreConnect([{ collection: "users" }]);
const [commenting, toggleCommenting] = useState(false);
const [newComment, setNewComment] = useState("");
const [users, setUsers] = useState(null);
const [op, setOp] = useState(null);
const [showProfile, setShowProfile] = useState(false);
const { docId, userId, text, likes, comments, time, image } = howl;
const getUsers = useSelector((state) => state.firestore.ordered.users);
const currentUser = useSelector((state) => state.firebase.auth);
// establish user that posted this howl (op = original poster)
useEffect(() => {
users ? setOp(users.find((doc) => doc.uid === userId)) : setUsers(getUsers);
}, [users, op, userId, getUsers]);
const handleLike = () => {
const index = likes.indexOf(currentUser.uid);
let newLikes = [...likes];
if (index > 0) {
newLikes.splice(index, 1);
} else if (index === 0) {
if (likes.length > 1) {
newLikes.splice(index, 1);
} else {
newLikes = [];
}
} else {
newLikes = [...newLikes, currentUser.uid];
}
firestore.collection("howls").doc(docId).update({ likes: newLikes });
};
const handleChange = (event) => {
setNewComment(event.currentTarget.value);
};
const submitComment = (event) => {
event.preventDefault();
const { id } = event.currentTarget;
const { uid, photoURL } = currentUser;
const resetComment = () => {
toggleCommenting(!commenting);
setNewComment("");
};
firestore
.collection("howls")
.doc(id)
.update({
comments: [
...comments,
{ uid: uid, photoURL: photoURL, text: newComment },
],
})
.then(() => resetComment());
};
return (
<div className="howl">
<div className="avatar-container">
<button className="show-profile" onClick={() => setShowProfile(true)}>
<Avatar
photoURL={op ? op.photoURL : ""}
displayName={op ? op.displayName : ""}
/>
</button>
</div>
<div className="name-text-img-container">
<p className="userName">
{op && op.displayName} - {timeCalc(Date.now(), time)}
</p>
<p className="howl-text">{text}</p>
<div className="img-container">
{image && <img src={image} alt="user uploaded" className="img" />}
</div>
<div className="buttons-container">
<div className="buttons">
<button className="comment-button">
<FontAwesomeIcon
icon={faComment}
className="image-icon"
onClick={() => toggleCommenting(!commenting)}
/>
</button>
<button className="like-button" onClick={handleLike}>
<FontAwesomeIcon
icon={faStar}
className={
currentUser && likes.includes(currentUser.uid)
? "image-icon liked"
: "image-icon"
}
/>
</button>
<p>{likes.length > 0 && likes.length}</p>
</div>
</div>
{commenting && (
<CommentInput
submitComment={submitComment}
docId={docId}
toggleCommenting={toggleCommenting}
commenting={commenting}
handleChange={handleChange}
newComment={newComment}
/>
)}
{showProfile && (
<ViewProfile
user={op}
close={() => setShowProfile(false)}
update={false}
/>
)}
<div className="comments">
{comments &&
comments.map((comment, index) => {
return <Comment key={`comment${index}`} comment={comment} />;
})}
</div>
</div>
</div>
);
};
export default Howl;
I want to get input from a user and compare it with the response I am getting from API, and conditionally render the information if it match or just show a sorry message,(the API only contain 1 set of a data object including 4 value) let me know what am I missing.
here is my code
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatInput, setVatInput] = useState("");
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
const [result, setResult] = useState(false);
const handelVatState = (event) => {
setVatInput(event.target.value);
};
const closeModalHandler = () => {
setResult(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("Some URL")
.then((response) => {
setResponseVatState(response.data.response.data.VATNumber);
setResponseCountryCodeState(response.data.CountryCode);
})
.catch((error) => {
console.log(error);
});
};
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{result ? (
<div>{vatInput}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
and the error is
react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
so I get the input from the user and set it with hooks, then with Axios call get my data, then I split the string with
const inputCountryCode = vatInput.substring(0, 2);
const inputVatCode = vatInput.substring(2);
to compare with the input I have, if it's the same then render the data if not just render the sorry message
You have a couple of issues, the main of which resulting in Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. is due to an infinite loop of component re-rendering, which you force by setting state directly in the function body.
More specifically, this code:
if (
inputCountryCode === responseCountryCodeState &&
inputVatCode === responseVatState
) {
setResult(true);
} else {
setResult(false);
}
force react to re-evaluate the component because you're changing its state by using setResult. When react starts rendering the new body it yet again encounters setResult which results in a new update and re-render which, as you see, leads to a never-ending loop.
Furthermore, you don't need to save the request response to the component state at all, as it is relevant just for the calculation, which is needed only in the form submit handler itself. So, you should ditch the
const [responseVatState, setResponseVatState] = useState("");
const [responseCountryCodeState, setResponseCountryCodeState] = useState("");
state variables altogether. The only state you need except the input value is the validation result.
Also, you have a typo: setResponseVatState(response.data.response.data.VATNumber); should be setResponseVatState(response.data.VATNumber);.
Try this:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(false);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(false);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("[URL]")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
<label className="text-muted">Result : </label>
{isVatValid ? (
<div>{vatValue}</div>
) : (
<div clicked={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
)}
</div>
);
}
export default Form;
Also, I suppose <div clicked={closeModalHandler}> should be <div onClick={closeModalHandler}>?
EDIT:
Here is your solution after comments:
import React, { useState } from "react";
import axios from "axios";
function Form() {
const [vatValue, setVatValue] = useState("");
const [isVatValid, setIsVatValid] = useState(null);
const handelVatState = (event) => {
setVatValue(event.target.value);
};
const closeModalHandler = () => {
setIsVatValid(null);
};
const onFormSubmit = (event) => {
event.preventDefault();
axios
.get("https://vat.erply.com/numbers?vatNumber=BG999999999")
.then((response) => {
const inputCountryCode = vatValue.substring(0, 2);
const inputVatCode = vatValue.substring(2);
const { VATNumber, CountryCode } = response.data;
if (inputCountryCode === CountryCode && inputVatCode === VATNumber) {
setIsVatValid(true);
}
else {
setIsVatValid(false);
}
})
.catch((error) => {
console.log(error);
});
};
const getResultRepresentation = () => {
if (isVatValid === null) {
return null;
}
if (isVatValid) {
return (
<>
<label className="text-muted">Result: </label>
<div>{vatValue}</div>
</>
);
}
else {
return (
<div onClick={closeModalHandler}>
<span> Sorry !!! Please Insert corect VAT Number</span>
</div>
);
}
}
return (
<div >
<h4>VAT Validator</h4>
<form onSubmit={onFormSubmit}>
<label className="text-muted">Please Enter A Vat Number:</label>
<input
type="text"
name="VatInput"
placeholder="Please Enter A Vat Number"
value={vatValue} // <= missing
onChange={handelVatState}
/>
<br />
<input type="submit" value="Let'Go" />
</form>
{getResultRepresentation()}
</div>
);
}
export default Form;
And here is a CodeSandbox to test it out.