I'm pretty new to using React Js and Javascript as well. I'm wondering how the Dialog below could be refactored into a reusable component since I have duplicate code for another form. Basically, I make a call to my API, and then once the "fetch" is completed, the alert dialog pops out. I was originally thinking of having a separate component and passing along props, but I can't wrap my head around passing props such as dialogText to DialogContent itself.
I posted the Dialog itself below, and also the entire page so there is proper context.
Dialog
<Dialog open={openDialog} onClose={handleCloseDialog}>
<DialogTitle>{"Title"}</DialogTitle>
<DialogContent>
<DialogContentText>
{dialogText}
</DialogContentText>
</DialogContent>
<DialogActions>
<button onClick={handleCloseDialog} autoFocus>Close</button>
</DialogActions>
</Dialog>
Entire code
import React, { useState } from "react";
import Dialog from "#material-ui/core/Dialog";
import DialogContentText from "#material-ui/core/DialogContentText";
import DialogTitle from "#material-ui/core/DialogTitle";
import DialogActions from "#material-ui/core/DialogActions";
import DialogContent from "#material-ui/core/DialogContent";
import Loader from "../../assets/Button";
import handleSaveUser from "../hooks/handleSaveUser";
const NewsLetterForm = () => {
const [loading, setLoading] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const [dialogText, setDialogText] = useState("");
const [postParams, setPostParams] = useState({
firstName: "",
lastName: "",
email: "",
});
const handleCloseDialog = () => { setOpenDialog(false); };
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
const formData = new FormData(e.target);
const object = {
firstName: formData.get("firstName") ?? "",
lastName: formData.get("lastName") ?? "",
email: formData.get("email") ?? "",
}
setPostParams(postParams);
const apiResponse = await handleSaveUser("newsletter__form", object);
setDialogText(apiResponse);
setOpenDialog(true);
setLoading(false);
};
return (
<form
id='newsletter__form'
onSubmit={handleSubmit}
>
<label htmlFor='firstName' />
<input
name='firstName'
className='__input'
type='text'
id='firstName'
placeholder='First Name'
required
/>
<label htmlFor='lastName' />
<input
name='lastName'
className='__input'
type='text'
id='lastName'
placeholder='Last Name'
required
/>
<label htmlFor='email' />
<input
name='email'
className='__input'
type='text'
id='email'
placeholder='Email'
required
/>
<button
disabled={loading}
type="submit"
value="Subscribe"
>{loading ? <Loader /> : <>Subscribe</>}
</button>
<Dialog open={openDialog} onClose={handleCloseDialog}>
<DialogTitle>{"Some Title"}</DialogTitle>
<DialogContent>
<DialogContentText>
{dialogText}
</DialogContentText>
</DialogContent>
<DialogActions>
<button onClick={handleCloseDialog} autoFocus>Close</button>
</DialogActions>
</Dialog>
</form>
);
};
export default NewsLetterForm;
You could use the props to pass the title content etc to your CustomDialog
CustomDialog.jsx
const CustomDialog = ({ isOpen, onClose, title, content }) => {
return (
<Dialog open={isOpen} onClose={onClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
<DialogContentText>{content}</DialogContentText>
</DialogContent>
<DialogActions>
<button onClick={onClose} autoFocus>
Close
</button>
</DialogActions>
</Dialog>
);
};
Which you can use in your NewsLetterForm like so
import CustomDialog from "./CustomDialog";
const NewsLetterForm = () => {
const [loading, setLoading] = useState(false);
const [openDialog, setOpenDialog] = useState(false);
const [dialogText, setDialogText] = useState("");
const handleSubmit = async (e) => {
e.preventDefault();
...
setPostParams(postParams);
const apiResponse = await handleSaveUser("newsletter__form", object);
setDialogText(apiResponse);
setOpenDialog(true);
setLoading(false);
};
return (
<form>
...
<CustomDialog
isOpen={openDialog}
onClose={handleCloseDialog}
title="Some title"
content={dialogText}
/>
</form>
);
};
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)
}
import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [name, setName] = useState("");
const [username, setUsername] = useState("");
return (
<div className="App">
{" "}
<div className="addUser">
<input
type="text"
placeholder="Name..."
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button
onClick={() => {
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
name,
username,
})
);
}}
>
{" "}
Add User
</button>
</div>
);}
I am new to react and redux. After clicking the "Add User" button, new User data from inputs in the code will be added to the backend list. I want the values in input sections to be cleared after clicking the "Add User" button, but I don't know how to do.
you need to clear your state after click on submit button. for ex: set function like =>
const clearData = {
setName("")
setUsername("")
}
and pass the func to your onClick event.
onClick={clearData}
The following code will work perfectly fine.
Just assign value={name} and value={username} to both input types respectively and when you click Add User just clear the data in both the states.
import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [name, setName] = useState("");
const [username, setUsername] = useState("");
return (
<div className="App">
{" "}
<div className="addUser">
<input
type="text"
placeholder="Name..."
value={name}
onChange={(event) => {
setName(event.target.value);
}}
/>
<input
type="text"
placeholder="Username..."
value={username}
onChange={(event) => {
setUsername(event.target.value);
}}
/>
<button
onClick={() => {
setName("");
setUsername("");
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
name,
username,
})
);
}}
>
{" "}
Add User
</button>
</div>
);}
You can maintain a simple variable with list of form fields and can update the form state with the variable when you needed to clear form data. The below approach comes handy when you need to add additional fields as well.
import "./App.css";
import { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { addUser} from "./features/Users";
const formFields = { name: '', username: '' };
function App() {
const dispatch = useDispatch();
const userList = useSelector((state) => state.users.value);
const [params, setParams] = useState(formFields)
const handleChange = (e) => {
const { name, value } = e.target;
setParams({ ...params }, ...{[name]: value});
}
const clearForm = () => setParams(formFields);
return (
<div className="App">
<div className="addUser">
<input
type="text"
placeholder="Name..."
value={params.name}
onChange={(e) => handleChange(e)}
/>
<input
type="text"
placeholder="Username..."
value={params.username}
onChange={(e) => handleChange(e)}
/>
<button
onClick={() => {
dispatch(
addUser({
id: userList[userList.length - 1].id + 1,
...params
})
);
clearForm();
}}
>
{" "}
Add User
</button>
</div>
</div>
)
}
So I am trying to save some data into a mongodb data base using axios
I created a form to fill then when i click on save the data must be saved.
this is my try:
import auction1 from '../../Images/auction1.png'
import {useState} from 'react';
import './HomeScreen.css'
import {Button, Modal } from 'react-bootstrap';
import axios from 'axios';
const HomeScreen = ()=>{
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const [name, setName] = useState("");
const [price, setPrice] = useState(null);
const [deadline, setDeadLine] = useState("");
const [description, setDescription] = useState("");
const [img, setImg] = useState("");
const [imgMessage, setImgMessage] = useState(null);
const [error, setError] = useState(false);
const handleSubmit = async(e)=>{
e.preventDefault();
try{
const config = {
headers: {
"Content-Type": "application/json",
}
}
const {data} = await axios.post("http://localhost:7500/api/users/posts",
{name, img, deadline, price, description},
config
);
console.log(data);
}catch(error){
console.log(error.response.data.message);
}
};
const postDetails = (pic)=>{
if(!pic){
return setImgMessage('Please select a picture');
}
setImgMessage(null);
if(pic.type === 'images/jpeg' || pic.type==='image/png'){
const data = new FormData();
data.append('file', pic);
data.append('upload_preset', 'battta');
data.append('cloud_name', 'ChkounZed');
fetch("https://api.cloudinary.com/v1_1/ChkounZed/upload", {
method: 'post',
body: data
})
.then((res)=> res.json())
.then((data)=> {
console.log(data);
setImg(data.url.toString())
})
.catch((error)=>{
console.log(error);
});
}else{
return setImgMessage('Please select a picture');
}
};
return(
<div className="container bg">
<img src ={auction1} className='landing-image' />
<div style={{marginLeft:460}}>
<Button variant="primary" onClick={handleShow}>
Create Post
</Button>
</div>
<Modal show={show} onHide={handleClose}>
<form onSubmit={handleSubmit}>
<Modal.Header closeButton>
<Modal.Title>Create Post</Modal.Title>
</Modal.Header>
<Modal.Body>
<form >
<div className="form-group">
<label>Post Name:</label>
<input type="text" className="form-control" placeholder="Enter Name"
value={name} onChange={(e)=> setName(e.target.value)}/>
</div>
<div className="form-group">
<label>Post Images:</label>
<input type="file" className="form-control" multiple onChange="readURL(this)" accept="Image/*"
onChange={(e)=> postDetails(e.target.files[0])}/>
</div>
<div>
<label>Price:</label>
<input type="number" className="form-control" placeholder="TND"
value={price} onChange={(e)=> setPrice(e.target.value)}/>
</div>
<div>
<label>DeadLine:</label>
<input type="datetime-local" className="form-control"
value={deadline} onChange={(e)=> setDeadLine(e.target.value)}/>
</div>
<div>
<label>Description:</label>
<textarea className="form-control" rows="3"
value={description} onChange={(e)=> setDescription(e.target.value)}/>
</div>
</form>
</Modal.Body>
<Modal.Footer>
<button type="submit" className="btn btn-primary" data-bs-dismiss="modal" onClick={handleClose} >
Save Post
</button>
<button type="submit" className="btn btn-secondary" data-bs-dismiss="modal" onClick={handleClose}>
Close
</button>
</Modal.Footer>
</form>
</Modal>
</div>
)
};
export default HomeScreen;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
that was my react component and the problem is that i keep getting a message that says the post already exists.
this is my postController from the backend side:
const Post = require("../Models/postModel");
const asyncHandler = require("express-async-handler");
const savePost = asyncHandler(async(req,res)=>{
const {name, deadline, price, description, image} = req.body;
const postExists = await Post.findOne({image});
if(postExists){
res.status(400);
throw new Error('Post Already Exists');
}
const post = await Post.create({
name,
deadline,
price,
description,
image
});
if(post){
res.status(201).json({
_id: post._id,
name: post.name,
price: post.price,
image: post.image,
deadline: post.deadline,
description: post.description
});
}else{
res.status(400);
throw new Error('Error');
}
});
module.exports = {savePost};
I would be so grateful if you can give me hand of this and by the way i wanna make my post unique using the images and still did not know how
Can you provide more information? Your Post Model. What happens at the DB Collection? Is it empty?
The response of mongodb includes _id why not use findById instead of find({image}).
When u wipe your database and post the first time whats the response of your post to /api/users/posts?
To make some fields unique for the collection you can use unique like below:
const PlayerSchema = new mongoose.Schema({
name:{
type: String,
unique: true
}
})
Maybe I don't understand something.
Perhaps here need some ID or shouldn't repeat the names for input in react-hook-from.
Moreover, the registration form works great with exactly the same code, it seems to me that I have studied everything so thoroughly that there are no differences.
I have tried giving other names in the registration. But when the event is deleted, a redirect occurs, although it is not in the code at all for sign-in page.
Code component:
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import { schema } from '#src/shared/schema';
import Input from '#src/components/UI/Input/Input';
import Button from '#src/components/UI/Button/Button';
import Loader from '#src/components/Loader/Loader';
import { signIn } from '#src/store/actions/authActions';
const SignInPage = props => {
const loading = useSelector(state => state.auth.loading);
const error = useSelector(state => state.auth.error);
const dispatch = useDispatch();
const submitForm = auth => dispatch(signIn(auth));
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onBlur', resolver: yupResolver(schema) });
const submitSignInHandler = data => {
console.log(data);
submitForm(data);
};
return (
<div className="sign-in-page">
<h2 className="sign-in-page__title">Sign In</h2>
<form id="sign-in" className="sign-in-page__form" onSubmit={handleSubmit(submitSignInHandler)}>
<Input
label="Mail"
type="email"
errors={!!errors.email}
errorsMessage={errors?.email?.message}
placeholder="johnabrams#mail.com"
{...register('email')}
/>
<Input
label="Password"
type="password"
errors={!!errors.password}
errorsMessage={errors?.password?.message}
placeholder="d22DAsc4ee"
{...register('password')}
/>
{loading && <Loader />}
<Button type="submit" disabled={loading || error}>
Sign In
</Button>
{error && <p>{error.message}</p>}
</form>
</div>
);
};
export default SignInPage;
Similar code that works great:
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import { schema } from '#src/shared/schema';
import parsePhoneNumberFromString from 'libphonenumber-js';
import Input from '#src/components/UI/Input/Input';
import Button from '#src/components/UI/Button/Button';
import Loader from '#src/components/Loader/Loader';
import { signUp } from '#src/store/actions/authActions';
import { Redirect } from 'react-router';
const SignUpPage = props => {
const loading = useSelector(state => state.auth.loading);
const error = useSelector(state => state.auth.error);
const dispatch = useDispatch();
const submitForm = (info, auth) => dispatch(signUp(info, auth));
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ mode: 'onBlur', resolver: yupResolver(schema) });
const [successfulSubmit, setSuccessfulSubmit] = useState(false);
const normalizePhoneNumber = value => {
const phoneNumber = parsePhoneNumberFromString(value);
if (!phoneNumber) {
return value;
}
return phoneNumber.formatInternational();
};
const submitSignUpHandler = data => {
const info = { name: data.name, phone: data.phone };
const auth = { email: data.email, password: data.password };
submitForm(info, auth);
if (!loading && !error) {
setSuccessfulSubmit(true);
}
};
return (
<div className="sign-up-page">
<h2 className="sign-up-page__title">Sign Up</h2>
<form id="sign-up" className="sign-up-page__form" onSubmit={handleSubmit(submitSignUpHandler)}>
<Input
label="Name"
errors={!!errors.name}
errorsMessage={errors?.name?.message}
placeholder="John"
{...register('name')}
/>
<Input
label="Mail"
type="email"
errors={!!errors.email}
errorsMessage={errors?.email?.message}
placeholder="johnabrams#mail.com"
{...register('email')}
/>
<Input
label="Password"
type="password"
errors={!!errors.password}
errorsMessage={errors?.password?.message}
placeholder="d22DAsc4ee"
{...register('password')}
/>
<Input
label="Phone"
type="tel"
errors={!!errors.phone}
errorsMessage={errors?.phone?.message}
onChange={event => {
event.target.value = normalizePhoneNumber(event.target.value);
}}
placeholder="+7 999 777 20 20"
{...register('phone')}
/>
{loading && <Loader />}
<Button type="submit" disabled={loading || error}>
Sign Up
</Button>
{error && <p>{error.message}</p>}
</form>
{successfulSubmit && <Redirect to="/sign-in" />}
</div>
);
};
export default SignUpPage;errors={!!errors.name}
errorsMessage={errors?.name?.message}
placeholder="John"
{...register('name')}
/>
<Input
label="Mail"
type="email"
errors={!!errors.email}
errorsMessage={errors?.email?.message}
placeholder="johnabrams#mail.com"
{...register('email')}
/>
<Input
label="Password"
type="password"
errors={!!errors.password}
errorsMessage={errors?.password?.message}
placeholder="d22DAsc4ee"
{...register('password')}
/>
<Input
label="Phone"
type="tel"
errors={!!errors.phone}
errorsMessage={errors?.phone?.message}
onChange={event => {
event.target.value = normalizePhoneNumber(event.target.value);
}}
placeholder="+7 999 777 20 20"
{...register('phone')}
/>
{loading && <Loader />}
<Button type="submit" disabled={loading || error}>
Sign Up
</Button>
{error && <p>{error.message}</p>}
</form>
{successfulSubmit && <Redirect to="/sign-in" />}
</div>
);
};
export default SignUpPage;
I don't understand the reasons, but I created a new schema, and the form started working.
Here is my Login component:
const Login = () => {
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
return (
<div>
<p>Login</p>
<input
type="text"
onChange={(e) => {
setUser(e.target.value);
}}
/>
<input
type="password"
onChange={(e) => {
setPass(e.target.value);
}}
/>
<button onClick={submit(user, pass)}>
Submit
</button>
</div>
);
};
It renders on my webpage, but it calls the submit() function whenever I input to these two: text and password. Looking at my code, I've only set the onClick to call the submit function.
Is there something wrong with my code?
EDIT: Removed classNames for easier viewing
You are calling the submit function on every render. onClick takes a function, but you are directly calling a function.
<button onClick={submit(user, pass)}>
Submit
</button>
will be replaced by
<button onClick={()=>submit(user, pass)}>
Submit
</button>
try :
const Login = () => {
const [user, setUser] = useState("");
const [pass, setPass] = useState("");
const onSubmit = () => {
submit(user,pass)
}
return (
<div>
<p>Login</p>
<input
type="text"
onChange={(e) => {
setUser(e.target.value);
}}
/>
<input
type="password"
onChange={(e) => {
setPass(e.target.value);
}}
/>
<button onClick={onSubmit}>
Submit
</button>
</div>
);
};