search filter in react js - javascript

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;

Related

why every time is visit my product details page it is auto scrolling to the bottom of the page?

Whenever I'm clicking on a card to go to the product details page it is automatically going to the bottom of the next page without even scrolling it here is the sample of my code
import React from "react";
import { Link } from "react-router-dom";
import { Rating } from "#material-ui/lab";
const ProductCard = ({ product }) => {
const options = {
value: product.ratings,
readOnly: true,
precision: 0.5,
};
return (
<Link className="productCard" to={`/product/${product._id}`}>
<img src={product.images[0].url} alt={product.name} />
<p>{product.name}</p>
<div>
<Rating {...options} />
<span className="productCardSpan">
{""}({product.numOfReviews} Reviews)
</span>
</div>
<span>{`₹${product.price}/-`}</span>
</Link>
);
};
export default ProductCard;
product details page is starting from here
import React, { useEffect, useState } from "react";
import Carousel from "react-material-ui-carousel";
import "./ProductDetails.css";
import { useSelector, useDispatch } from "react-redux";
import {
clearErrors,
getProductDetails,
newReview,
} from "../../actions/productAction";
import ReviewCard from "./ReviewCard.js";
import Loader from "../layout/Loader/Loader";
import { useAlert } from "react-alert";
import MetaData from "../layout/MetaData";
import { addItemsToCart } from "../../actions/cartAction";
import {
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Button,
} from "#material-ui/core";
import { Rating } from "#material-ui/lab";
import { NEW_REVIEW_RESET } from "../../constants/productConstants";
const ProductDetails = ({ match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const { product, loading, error } = useSelector(
(state) => state.productDetails
);
const { success, error: reviewError } = useSelector(
(state) => state.newReview
);
const options = {
size: "large",
value: product.ratings,
readOnly: true,
precision: 0.5,
};
const [quantity, setQuantity] = useState(1);
const [open, setOpen] = useState(false);
const [rating, setRating] = useState(0);
const [comment, setComment] = useState("");
const increaseQuantity = () => {
if (product.Stock <= quantity) return;
const qty = quantity + 1;
setQuantity(qty);
};
const decreaseQuantity = () => {
if (1 >= quantity) return;
const qty = quantity - 1;
setQuantity(qty);
};
const addToCartHandler = () => {
dispatch(addItemsToCart(match.params.id, quantity));
alert.success("Item Added To Cart");
};
const submitReviewToggle = () => {
open ? setOpen(false) : setOpen(true);
};
const reviewSubmitHandler = () => {
const myForm = new FormData();
myForm.set("rating", rating);
myForm.set("comment", comment);
myForm.set("productId", match.params.id);
dispatch(newReview(myForm));
setOpen(false);
};
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
if (reviewError) {
alert.error(reviewError);
dispatch(clearErrors());
}
if (success) {
alert.success("Review Submitted Successfully");
dispatch({ type: NEW_REVIEW_RESET });
}
dispatch(getProductDetails(match.params.id));
}, [dispatch, match.params.id, error, alert, reviewError, success]);
console.log(product.images);
return (
<>
{loading ? (
<Loader />
) : (
<>
<MetaData title={`${product.name} -- ECOMMERCE`} />
<div className="ProductDetails">
<div>
{product.images &&
product.images.map((item, i) => (
<img
className="CarouselImage"
key={i}
src={item.url}
alt="product"
/>
))}
</div>
<div>
<div className="detailsBlock-1">
<h2>{product.name}</h2>
<p>Product # {product._id}</p>
</div>
<div className="detailsBlock-2">
<Rating {...options} />
<span className="detailsBlock-2-span">
{" "}
({product.numOfReviews} Reviews)
</span>
</div>
<div className="detailsBlock-3">
<h1>{`₹${product.price}`}</h1>
<div className="detailsBlock-3-1">
<div className="detailsBlock-3-1-1">
<button onClick={decreaseQuantity}>-</button>
<input readOnly type="number" value={quantity} />
<button onClick={increaseQuantity}>+</button>
</div>
<button
disabled={product.Stock < 1 ? true : false}
onClick={addToCartHandler}
>
Add to Cart
</button>
</div>
<p>
Status:
<b className={product.Stock < 1 ? "redColor" : "greenColor"}>
{product.Stock < 1 ? "OutOfStock" : "InStock"}
</b>
</p>
</div>
<div className="detailsBlock-4">
Description : <p>{product.description}</p>
</div>
<button onClick={submitReviewToggle} className="submitReview">
Submit Review
</button>
</div>
</div>
<h3 className="reviewsHeading">REVIEWS</h3>
<Dialog
aria-labelledby="simple-dialog-title"
open={open}
onClose={submitReviewToggle}
>
<DialogTitle>Submit Review</DialogTitle>
<DialogContent className="submitDialog">
<Rating
onChange={(e) => setRating(e.target.value)}
value={rating}
size="large"
/>
<textarea
className="submitDialogTextArea"
cols="30"
rows="5"
value={comment}
onChange={(e) => setComment(e.target.value)}
></textarea>
</DialogContent>
<DialogActions>
<Button onClick={submitReviewToggle} color="secondary">
Cancel
</Button>
<Button onClick={reviewSubmitHandler} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
{product.reviews && product.reviews[0] ? (
<div className="reviews">
{product.reviews &&
product.reviews.map((review) => (
<ReviewCard key={review._id} review={review} />
))}
</div>
) : (
<p className="noReviews">No Reviews Yet</p>
)}
</>
)}
</>
);
};
export default ProductDetails;
I tried linking in it but it won't work and someone told me to use useRef but i don't know how to use it
You are using React router dom. In React Router there is the problem that if we redirect to the new route, it won't automatically take you to the top of the page. Such behavior is normal when you navigate between pages.
Since you are using functional components
Try to use the following window scroll to the top when the component mounts.
useEffect(() => {
window.scrollTo(0, 0)
}, [])
Browser scroll your page.
If you don't want to let browser auto scroll your page,
use History.scrollRestoration method
Prevent automatic page location restoration
if (history.scrollRestoration) {
history.scrollRestoration = 'manual';
}
Read more at MDN History.scrollRestoration

How can I make each student add their own identifier tags?

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>
);
}

How to put values from json into inputs by default?

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

All comment forms submit the same docId

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;

How to Delete "Input type File textfield"

What i am trying to achieve is that i want to allow users to upload as many or limited files. To add he can always add the "Input file field".
Everything works correctly but when i try to delete it. It does not delete to "Input field" of SPECIDIC INDEX or Specific ID provided but it deletes the last field always.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [fields, setFields] = useState([{ files: null }]);
function handleChange(i, event) {
const values = [...fields];
values[i].files = event.target.[0];
console.log(values);
setFields(values);
}
function handleAdd() {
const values = [...fields];
values.push({ value: null });
setFields(values);
}
function handleRemove(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button type="button" onClick={() => handleAdd()}>
+
</button>
{fields.map((field, idx) => {
return (
<div key={`${field}-${idx}`}>
<input
type="text"
defaultValue={field.files || ""}
onChange={(e) => handleChange(idx, e)}
/>
<button type="button" onClick={() => handleRemove(idx)}>
X
</button>
</div>
);
})}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
The problem is the map KEY you should use a specific id instead of the index or the file object you can find below the example with the uuid package.
EXAMPLE :
import { useState } from "react";
import "./styles.css";
import { v4 as uuidv4 } from "uuid";
const App = () => {
const [fields, setFields] = useState([{ id: uuidv4(), files: "" }]);
function handleChange(i, event) {
setFields(
fields.map((input) =>
input.id === i ? { ...input, files: event.target.value } : input
)
);
}
function handleAdd() {
const values = [...fields];
values.push({ id: uuidv4(), files: "" });
setFields(values);
}
function handleRemove(idx) {
setFields(fields.filter((_, i) => i !== idx));
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button type="button" onClick={() => handleAdd()}>
+
</button>
{fields.map((field, idx) => {
return (
<div key={field.id}>
<input
type="text"
value={field.files}
onChange={(e) => handleChange(field.id, e)}
/>
<button type="button" onClick={() => handleRemove(idx)}>
X
</button>
</div>
);
})}
</div>
);
};
export default App;

Categories