I did CODESANDBOX with your code example: codesandbox.io/s/keen-lehmann-dtwz2y?file=/src/App.tsx, So you can see how it turned out! This select don't work when I want to change value. Please show me how to make the same select but working!
This my Select.tsx:
import styles from "./select.module.scss"
import { useForm } from "react-hook-form";
import { useRef, useState } from "react";
interface IFormInput {
sortType: string;
}
const Select = () => {
const { register, handleSubmit } = useForm<IFormInput>();
const [isOpened, setIsOpened] = useState(false);
const [checked, setChecked] = useState(true);
const dropdownRef = useRef(null);
const handleClick = (e:any) => {
setChecked(e.target.value);
e = true;
};
const onSubmit = (data: IFormInput) => {
console.log(data);
};
const handleOnClick = (event: any) => {
console.log("test");
setIsOpened(!isOpened);
event.stopPropagation();
event.preventDefault();
};
return (
<div className={styles.select}>
<p className={styles.pSelect}>Who are creating the ad?</p>
<div className={styles.body}>
<span
ref={dropdownRef}
onClick={handleOnClick}
className={isOpened ? "expanded dropdown-el" : "dropdown-el"}
>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="radio"
name="radio"
value="Freelancer"
id="sort-relevance"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
<label htmlFor="sort-relevance">Freelancer</label>
<input
type="radio"
name="radio"
value="Client"
id="sort-best"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
<label htmlFor="sort-best">Client</label>
</form>
</span>
</div>
</div>
);
}
export default Select
export default function App() {
const { register, handleSubmit } = useForm<IFormInput>();
const [isOpened, setIsOpened] = useState(false);
const dropdownRef = useRef(null);
const [checked, setChecked] = useState("Client"); // Set first initial value for select
const handleClick = (e) => {
setChecked(e.target.value);
e.target.defaultChecked = true;
};
const onSubmit = (data: IFormInput) => {
console.log(data);
};
const handleOnClick = (event: any) => {
console.log("test");
setIsOpened(!isOpened);
event.stopPropagation();
event.preventDefault();
};
return (
<div>
<span
ref={dropdownRef}
onClick={handleOnClick}
className={isOpened ? "expanded dropdown-el" : "dropdown-el"}
>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="radio"
name="radio"
id="sort-relevance"
checked={checked === "Freelancer"}
onClick={(e) => handleClick(e)}
/>
<label
htmlFor="sort-relevance"
onClick={() => setChecked("Freelancer")}
>
Freelancer
</label>
<input
type="radio"
name="radio"
id="sort-best"
checked={checked === "Client"}
onClick={(e) => handleClick(e)}
/>
<label htmlFor="sort-best" onClick={() => setChecked("Client")}>
Client
</label>
</form>
</span>
</div>
);
}
Related
I just want to preface this that I am learning JavaScript and React so this is all very new to me.
I am building a "simple" movie rating app and need to be able to push a review to a div "on submit" and cannot figure out how to do so. I have tried using update state in react and/or creating functions to try to accomplish this and cannot figure out how to do this for the life of me. I did somewhat succeed using the latter method, but was getting errors about using unique key props. The other problem was I am to use a star-rating component and when I submitted the review, it wasn't pushing that to the div. This is where I'm at currently:
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
console.log("Form Submitted");
};
return (
<div className="form-container">
<Stars />
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
</div>
);
}
// This is what I have in my Stars component:
import React, { useState } from "react";
import { FaStar} from 'react-icons/fa'
const Stars = () => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
return(
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return <label>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => setRating(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>;
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars```
Here is the working version of your code. You should use key in your map and e.preventDefault() in your form submit function. As final touch you should set another state inside your form submit and show this value in a div or some html element. Also I see that you want to get child state into parent so you can call callback for this https://codesandbox.io/embed/brave-euler-ybp9cx?fontsize=14&hidenavigation=1&theme=dark
ReviewForm.js
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const [value, setValue] = useState("");
const [star, setStar] = useState();
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault();
setValue(reviews + " with " + star + " star ");
};
return (
<div className="form-container">
<Stars setStar={setStar} />
<Form onSubmit={onSubmit}>
<Input
className="form-control"
type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
<div>{value}</div>
</Form>
</div>
);
}
Stars.js
const Stars = ({ setStar }) => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
const handleClick = (ratingValue) => {
setRating(ratingValue);
setStar(ratingValue);
};
return (
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return (
<label key={i}>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => handleClick(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>
);
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars;
You probably are seeing a page refresh when you press the submit button. This is the default behavior of HTML forms.
When using React or any front-end framework, you'd want to handle the form submission yourself rather than letting the browser submit your forms.
In your onSubmit function, add the following line
e.preventDefult()
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
};
Your code will work perfectly.
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [Reviews, setReviews] = useState("");
const [ReviewsRating, setReviewsRating] = useState(5);
const [Reviews_, setReviews_] = useState([]);
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
//After upload to the server
setReviews_([Reviews, ...Reviews_]
};
return (
<div className="form-container">
<Stars getRating={getRating}/>
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
<div class="reviews">
{Reviews_.map(item => <div> {item}</div> )}
</>
</div>
);
}```
Then to get the stars rating value use props like...
And make sure you call that property (function) inside your Starts component
const getRating =(value)=>{
setReviewsRating(value)
}
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
What the code looks like rendering the button to show the form
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { deleteSong, getSongs, updateSong } from '../../store/song';
import ReactAudioPlayer from 'react-audio-player';
import { useHistory } from 'react-router';
import SongForm from '../AddSongForm';
import EditSongForm from '../EditSongForm';
const SongList = () => {
const [addShowForm, setAddShowForm] = useState(false);
const [editShowForm, setEditShowForm] = useState(false);
const history = useHistory()
const dispatch = useDispatch();
const songsObj = useSelector((state) => state.songState.entries);
const songs = Object.values(songsObj)
const user = useSelector((state) => state.session.user);
const CurrentUserId = user?.id
const remove = (e) => {
dispatch(deleteSong(e.target.id));
}
const addFormCheck = (e) => {
if (addShowForm) setAddShowForm(false)
if (!addShowForm) setAddShowForm(true)
}
const editFormCheck = (e) => {
if (editShowForm) setEditShowForm(false)
if (!editShowForm) setEditShowForm(true)
}
useEffect(() => {
dispatch(getSongs());
}, [dispatch]);
return (
<div>
<div>
<button onClick={addFormCheck}>add a song</button>
{addShowForm ?
<SongForm />
: null}
</div>
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div className='songdetails' key={id}>
<p key={id}>songName={songName}</p>
<ReactAudioPlayer
src={songLink}
autoPlay
controls
key={songLink}
/>
{userId === CurrentUserId ?
<>
<div>
<button id={id} onClick={remove}>remove</button>
</div>
<div>
<button id={id} onClick={editFormCheck}>edit</button>
{editShowForm ?
<EditSongForm props={id} />
: null}
</div>
</>
: null}
</div>
))}
</ol>
</div>
);
};
export default SongList;
The actual form
import { useState } from "react";
import { useDispatch } from "react-redux";
import { updateSong } from "../../store/song";
import { useSelector } from "react-redux";
const EditSongForm = ({ props }) => {
console.log(props)
const dispatch = useDispatch();
const [songName, setSongName] = useState("");
const [songLink, setSongLink] = useState("");
const [errors, setErrors] = useState([]);
const reset = () => {
setSongName("");
setSongLink("");
// setAlbumName('');
// setArtistName('')
};
const user = useSelector((state) => state.session.user);
const userId = user?.id
const handleSubmit = async (e) => {
e.preventDefault();
const updatedSongDetails = {
id: props,
songName,
songLink,
userId
};
let updatedSong = await dispatch(updateSong(updatedSongDetails))
.catch(async (res) => {
const data = await res.json()
if (data && data.errors) setErrors(data.errors)
})
reset();
};
return (
<div className="inputBox">
<h1>Update A Song</h1>
<ul>
{errors.map((error, idx) => <li className='errors' key={idx}>{error}</li>)}
</ul>
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setSongName(e.target.value)}
value={songName}
placeholder="Song Name"
name="Song Name"
/>
<input
type="text"
onChange={(e) => setSongLink(e.target.value)}
value={songLink}
placeholder="Song Link"
name="Audio File"
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default EditSongForm;
Right now when I have a list of songs and click the button for the edit form to appear it applies to the entire list if I have more than one song uploaded. I'm not sure how to make it specific enough so only one form opens at a time.
The solution was to create a component for specific song details and then render that in the .map.
<h1>Song List</h1>
<ol>
{songs.map(({ id, songName, songLink, userId }) => (
<div>
<SpecificSong id={id} songName={songName} songLink={songLink} userId={userId} />
</div>
))}
</ol>
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 have this button here
<button className={Classes.Button}
disabled={!isEnabled}
type="submit">
{buttonText}
</button>
which should be disabled or enable after checking the value of some of my inputs.
the conditions
const canBeSubmitted = () => {
return (
customerData.firstNameState.length > 0 && // TextInput
customerData.emailState.length > 0 && // TextInput
customerData.companeyState.length > 0 && // TextInput
customerData.selected.length > 0 && // Dropdown
customerData.agree === true // checkbox for terms
);
};
let isEnabled = canBeSubmitted();
BTW: The agree checkbox is checked by its handler and works fine.
The agree value is false in the state and the handler
const handleChange = (event) => {
const field = event.target.id;
if (field === "firstName") {
setFirstName({ firstName: event.target.value });
} else if (field === "email") {
setEmail({ email: event.target.value });
} else if (field === "country") {
setSelected({ country: event.target.value });
} else if (field === "agree") {
setAgree(!agree);
console.log(agree);
}
};
but always return false. what am I missing?
Please help me out
If I'm correct, your 'state' isn't changing because of how you're changing 'state variables' in handleChange fat arrow function.
I could be wrong depending on how your 'state' is structured.
I'm assuming your 'state' is structured like this.
const [firstName, setFirstName] = useState("");
const [email, setEmail] = useState("");
const [country, setCountry] = useState("");
const [agree, setAgree] = useState(false);
Fix your handleChange function.
// Commented out your possibly erroneous code.
const handleChange = (event) => {
const field = event.target.id;
if (field === "firstName") {
// Fix here.
// setFirstName({ firstName: event.target.value }); ❌
setFirstName(event.target.value); ✅
} else if (field === "email") {
// Fix here.
// setEmail({ email: event.target.value }); ❌
setEmail(event.target.value); ✅
} else if (field === "country") {
// Fix here.
// setSelected({ country: event.target.value }); ❌
setCountry(event.target.value); ✅
} else if (field === "agree") {
// Fix here.
// setAgree(!agree); ❌
setAgree(event.target.checked); ✅
console.log(agree);
}
};
You can then perform your validation like this:
const canBeSubmitted = () => {
return (
firstName.trim().length && // TextInput
email.trim().length && // TextInput
country.trim().length && // Dropdown
agree // checkbox for terms
);
};
It appears your also have a typo here for 'countryState':
customerData.companeyState.length > 0 && // TextInput
It looks like there is a full stop after && operator in your code.
customerData.selected.length > 0 &&. // Dropdown
Addendum
#Harry9345, you can as well get rid of the handleChange completely.
Full source code below. Demo: https://codesandbox.io/s/crimson-fog-vp19s?file=/src/App.js
import { useEffect, useState } from "react";
export default function App() {
const [firstName, setFirstName] = useState("");
const [email, setEmail] = useState("");
const [country, setCountry] = useState("");
const [agree, setAgree] = useState(false);
const canBeSubmitted = () => {
const isValid =
firstName.trim().length && // TextInput
email.trim().length && // TextInput
country.trim().length && // Dropdown
agree; // checkbox for terms
if (isValid) {
document.getElementById("submitButton").removeAttribute("disabled");
} else {
document.getElementById("submitButton").setAttribute("disabled", true);
}
console.log({ firstName, email, country, agree });
};
useEffect(() => canBeSubmitted());
return (
<div>
<form action="" method="post" id="form">
<label htmlFor="firstName">First name:</label>
<br />
<input
type="text"
id="firstName"
name="firstName"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<br />
<label htmlFor="email">Email Address:</label>
<br />
<input
type="email"
id="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<br />
<label htmlFor="country">Choose a country:</label>
<br />
<select
id="country"
name="country"
value={country}
onChange={(e) => setCountry(e.target.value)}
>
<option value="">Select..</option>
<option value="1">USA</option>
<option value="2">Canada</option>
<option value="3">Algeria</option>
</select>
<br />
<input
type="checkbox"
name="agree"
id="agree"
onClick={(e) => setAgree(e.target.checked)}
/>
<label htmlFor="agree"> I agree.</label>
<br />
<button type="submit" id="submitButton">
Submit
</button>
</form>
</div>
);
}
You should use state instead of variable:
I added some example:
import { useState } from "react";
const App = () => {
const [isDisabled, setIsDisabled] = useState(true);
const [checked, setChecked] = useState(false);
const canBeSubmitted = () => {
return checked ? setIsDisabled(true) : setIsDisabled(false);
};
const onCheckboxClick = () => {
setChecked(!checked);
return canBeSubmitted();
};
return (
<div className="App">
<input type="checkbox" onClick={onCheckboxClick} />
<button type="submit" disabled={isDisabled}>
Submit
</button>
</div>
);
};
export default App;
codesandbox
Of course it's just a sample of code and not very efficient.