My React form doesn't clear after submitting the form-data. where is the glitch? I can see form is successfully submitted to the mongoDB database with all the form content but problem is after submitting the value it doesn't clear the input filled.
import React,{useState} from 'react'
import { Paper, Typography, Button } from "#material-ui/core";
import FileBase from "react-file-base64";
import useStyles from "./styles"
import InputField from './InputField';
import { createPost } from "../../redux/actions/postAction";
import {useDispatch} from "react-redux";
const Form = () => {
const classes = useStyles();
const dispatch = useDispatch();
const [formData, setFormData] = useState({creator : "", title : "", message : "", tags : "", selectedFile : ""});
const handleSubmit = async (e) => {
e.preventDefault();
try {
dispatch(createPost(formData));
clearForm()
} catch (error) {
console.log(error);
}
}
const handleChange = (e) => {
setFormData({ ...formData, tags : e.target.value.split(",") , [e.target.name] : e.target.value })
}
const clearForm = () => {
setFormData({ creator : "", title : "", message : "", tags : "", selectedFile : ""})
}
return (
<Paper className={classes.paper}>
<Typography variant="h6" align="center">CREATE POST</Typography>
<form onSubmit={handleSubmit}>
<InputField
name="creator"
label="Creator"
handleChange={handleChange}
/>
<InputField
name="title"
label="Title"
handleChange={handleChange}
/>
<InputField
name="message"
label="Message"
handleChange={handleChange}
/>
<InputField
name="tags"
label="Tags"
handleChange={handleChange}
/>
<FileBase
type="file"
multiple={false}
onDone={ ({base64}) => setFormData({...formData, selectedFile : base64}) }
/>
<Button className={classes.btn} type="submit" variant="contained" color="primary" size="small" fullWidth>Create Posts</Button>
<Button variant="contained" color="secondary" size="small" fullWidth onClick={ clearForm }>Clear</Button>
</form>
</Paper>
)
}
export default Form
Try awaiting the dispatch before running clearForm().
EX:
try {
await dispatch(createPost(formData));
clearForm()
} catch (error) {
console.log(error);
}
}
Related
There is something wrong with the code but I didn't found what is it exactly, I guess the question is how to use useRef with Material UI?
I'm trying to code a login page, the code worked fine with original but when I used Material UI's it stopped.
The Code:
import { useContext, useRef } from "react";
import { Context } from "../../context/Context";
import axios from "axios";
import { useState } from "react"
import TextField from '#mui/material/TextField';
import Box from '#mui/material/Box';
import EmailIcon from '#mui/icons-material/Email';
import LockIcon from '#mui/icons-material/Lock';
export default function Login() {
const userRef = useRef();
const passwordRef = useRef();
const { dispatch, isFetching } = useContext(Context);
const [error, setError] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
try {
const res = await axios.post("/auth/login", {
username: userRef.current.value,
password: passwordRef.current.value,
});
dispatch({ type: "LOGIN_SUCCESS", payload: res.data });
} catch (err) {
dispatch({ type: "LOGIN_FAILURE" });
setError(true)
}
};
return (
<div className="login">
<form className="loginForm" onSubmit={handleSubmit}>
<Box>
<div>
<TextField
type="email"
required
id="outlined-basic"
label="Email"
variant="outlined"
ref={userRef}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<EmailIcon />
</InputAdornment>
),
}}
/>
<TextField
id="outlined-basic"
label="Password"
variant="outlined"
ref={passwordRef}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockIcon />
</InputAdornment>
),
}}
/>
</div>
</Box>
</form>
</div>
)
}
I don't know if using ref is correct in Material UI!
The better way to work with MUI TextField is to use a state like this instead of a ref:
const [userEmail, setUserEmail] = useState("")
return <TextField ... value={userEmail} onChange={(e)=>{setUserEmail(e.target.value)}} />
However if you still want to use a useRef instead, maybe the prop inputRef is what you're seeking
Hey I have tried a lot of things but I can't seem to get this thing to work I am trying to make a simple modal using material UI but whenever I try to change state it's showing me a blank white page. does anyone have any idea why is it happening here's my code
a
import {Button,Modal} from "#material-ui/core";
import {useState} from "react";
import RegisterBody from '../Register/RegisterForm'
import LoginBody from '../Login/LoginForm'
const UserModel = () => {
const [isOpen, setIsOpen] = useState(false)
const [isLoginModel, setLoginModel] = useState();
const [MainModelBody] = useState(LoginBody);
function handleRegister() {
if (!isLoginModel) {
console.log("Register")
//Todo: Send Register Request
} else {
MainModelBody.setState(RegisterBody)
setLoginModel(false)
}
}
function handleSignIn() {
if (isLoginModel) {
console.log("Login")
//Todo: send Sign in request
} else {
MainModelBody.setState(LoginBody)
setLoginModel(true)
}
}
function handleUserModel() {
setIsOpen(!isOpen)
}
return (
<>
<Button className="userActionButton" onClick={handleUserModel}>Sign In</Button>
<Modal open={isOpen}
onClose={!isOpen}
disablePortal
disableEnforceFocus
disableAutoFocus
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
<div className = 'UserModel'>
<LoginBody/>
<Button onClick={handleRegister}>Register</Button>
<Button onClick={handleSignIn}>Sign In</Button>
</div>
</Modal>
</>
);
}
export default UserModel
LoginBody
import {useState} from 'react';
import LoginElements from './LoginElements';
import {FormControl} from "#material-ui/core";
const LoginForm = ()=> {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const handleSubmit = (e) => {
e.preventDefault();
const user = {email, password}
console.log(user)
};
return (
<div className="form">
<FormControl noValidate autoComplete="off" onSubmit={handleSubmit}>
<LoginElements
email={email}
password={password}
setEmail={setEmail}
setPassword={setPassword}
/>
</FormControl>
</div>
);
}
export default LoginForm;
LoginElements
import {Button, TextField} from "#material-ui/core";
const LoginElements = (props) => {
return (
<>
<TextField label="Email" type="email" required value={props.email} onChange={(e) => props.setEmail(e.target.value)}/>
<br/>
<TextField label="Password" type="password" required value={props.password} onChange={(e) => props.setPassword(e.target.value)}/>
<br/>
<Button variant="contained" type="submit">Login</Button>
<Button variant="contained" type="register">Register</Button>
<br/>
<label>{props.email}</label>
<label>{props.password}</label>
</>
)
}
export default LoginElements;
RegisterBody
import {useState} from 'react';
import RegisterElements from './RegisterElements';
const LoginForm = ()=> {
const [email, setEmail] = useState('')
const [confirmEmail, setConfirmEmail] = useState('')
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const handleSubmit = (e) => {
e.preventDefault();
const user = {email, password}
console.log(user)
};
return (
<div className="form">
<form noValidate autoComplete="off" onSubmit={handleSubmit}>
<RegisterElements
email={email}
confirmEmail={email}
password={password}
confirmPassword={password}
setEmail={setEmail}
setConfirmEmail={setEmail}
setPassword={setPassword}
setConfirmPassword={setPassword}
/>
</form>
</div>
);
}
export default LoginForm;
Register Elements
import {Button, TextField} from "#material-ui/core";
const RegisterElements = (props) => {
return (
<>
<TextField label="Confirm Email" type="email" required value={props.email}
onChange={(e) => props.setEmail(e.target.value)}/>
<TextField label="Confirm Email" type="email" required value={props.confirmEmail}
onChange={(e) => props.setConfirmEmail(e.target.value)}/>
<br/>
<TextField label="Password"
type="password"
required value={props.password}
onChange={(e) => props.setPassword(e.target.value)}/>
<TextField label="Confirm Password" required value={props.confirmPassword}
onChange={(e) => props.setConfirmPassword(e.target.value)}/>
<br/>
<Button variant="contained" type="Login">Login</Button>
<Button variant="contained" type="submit">Login</Button>
<label>{props.email}</label>
<label>{props.password}</label>
</>
)
}
export default RegisterElements;
My apologies for bad code i am new with react
You are "toggling" the isLoginModel state but you only render LoginBody in the JSX.
You should be able to use the isLoginModel state and conditionally render either the LoginBody or RegisterBody component into the modal body. There's really no need for the extra state to hold a component reference.
import {Button,Modal} from "#material-ui/core";
import {useState} from "react";
import RegisterBody from '../Register/RegisterForm'
import LoginBody from '../Login/LoginForm'
const UserModel = () => {
const [isOpen, setIsOpen] = useState(false)
const [isLoginModel, setLoginModel] = useState(true); // login by default
function handleRegister() {
if (!isLoginModel) {
console.log("Register");
// TODO: Send Register Request
} else {
setLoginModel(false);
}
}
function handleSignIn() {
if (isLoginModel) {
console.log("Login");
// TODO: send Sign in request
} else {
setLoginModel(true);
}
}
function handleUserModel() {
setIsOpen(isOpen => !isOpen);
}
return (
<>
<Button className="userActionButton" onClick={handleUserModel}>Sign In</Button>
<Modal open={isOpen}
onClose={!isOpen}
disablePortal
disableEnforceFocus
disableAutoFocus
aria-labelledby="simple-modal-title"
aria-describedby="simple-modal-description"
>
<div className='UserModel'>
{isLoginModel ? <LoginBody /> : <RegisterBody />} // <-- condition render
<Button onClick={handleRegister}>Register</Button>
<Button onClick={handleSignIn}>Sign In</Button>
</div>
</Modal>
</>
);
}
import React, { useState } from "react";
import FileBase64 from "react-file-base64";
import { useDispatch } from "react-redux";
import { makeStyles } from "#material-ui/core/styles";
import { TextField, Select, Input, MenuItem, Button } from "#material-ui/core";
import { useForm, Controller } from "react-hook-form";
import { yupResolver } from "#hookform/resolvers/yup";
import * as yup from "yup";
import { updatePost } from "../actions/post";
const useStyles = makeStyles((theme) => ({
textField: {
marginBottom: theme.spacing(2),
},
buttons: {
marginTop: theme.spacing(2),
},
}));
const tags = ["fun", "programming", "health", "science"];
const postSchema = yup.object().shape({
title: yup.string().required(),
subtitle: yup.string().required(),
content: yup.string().min(20).required(),
tag: yup.mixed().oneOf(tags),
});
const EditPostForm = ({ history, post, closeEditMode }) => {
const dispatch = useDispatch();
const [file, setFile] = useState(post?.image);
const { register, handleSubmit, control, errors, reset } = useForm({
resolver: yupResolver(postSchema),
});
const onSubmit = (data) => {
const updatedPost = {
_id: post._id,
...data,
image: file,
};
dispatch(updatePost(post._id, updatedPost));
reset();
setFile(null);
closeEditMode();
};
const classes = useStyles();
return (
<div>
<form noValidate autoComplete="off" onSubmit={handleSubmit(onSubmit)}>
<TextField
id="title"
label="Başlık"
name="title"
variant="outlined"
className={classes.textField}
size="small"
{...register('title')}
error={errors?.title ? true : false}
fullWidth
defaultValue={post?.title}
/>
<TextField
id="subtitle"
label="Alt Başlık"
name="subtitle"
variant="outlined"
className={classes.textField}
size="small"
{...register('subtitle')}
error={errors?.subtitle ? true : false}
fullWidth
defaultValue={post?.subtitle}
/>
<Controller
render={({field}) => (
<Select
{...field}
input={<Input />}
className={classes.textField}
fullWidth
>
{
tags.map((tag, index) => (
<MenuItem {...field} key={index} value={tag}>
{tag}
</MenuItem>
))
}
</Select>
)}
name='tag'
control={control}
error={errors?.tag ? true : false}
defaultValue={tags[0]}
/>
<TextField
id="content"
label="İçerik"
name="content"
multiline
size="small"
{...register('content')}
rows={16}
className={classes.textField}
variant="outlined"
error={errors?.content ? true : false}
fullWidth
defaultValue={post?.content}
/>
<FileBase64 multiple={false} onDone={({ base64 }) => setFile(base64)} />
<div className={classes.buttons}>
<Button color="primary" variant="outlined" onClick={closeEditMode}>
Vazgeç
</Button>{" "}
<Button color="secondary" variant="outlined" type="submit" >
Kaydet
</Button>
</div>
</form>
</div>
);
};
export default EditPostForm;
I have EditPostForm component, component doesn't give any error but when I tried to submit my form onSubmit function is not triggered.
I used react-hook-form to create my form and I used material UI components inside form.
When I Click button which has type submit does not trigger onSubmit function which is called inside of handleSubmit. Why onSubmit is not triggered?
onSubmit isn't triggered because you may have form errors
You can get errors from formState object (const { formState } = useForm(...))
And then use error={formState.errors?.content ? true : false} in your code
https://react-hook-form.com/api/useform/formstate
See an example here
https://codesandbox.io/s/keen-burnell-2yufj?file=/src/App.js
You need to pass onSubmit and onError both.
Like this:
onPress={handleSubmit(onSubmit, onErrors)}
I faced the same error, the problem was that, my register were Boolean and my input was string, and since the value was not required It didn't show errors until I figure out the problem and change register from Boolean to string
I am using react-hook-form V7 with Material UI to create a simple form however, there is a weird behavior when setting the mode in useForm({mode: ".."})
Here is my code snippet:
import { yupResolver } from "#hookform/resolvers/yup";
import { Button, Grid, Link, TextField, Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
import Alert from "#material-ui/lab/Alert";
import { Controller, useForm } from "react-hook-form";
import { _MESSAGES } from "src/constants/messages";
import * as yup from "yup";
import AuthContainer from "./AuthContainer";
// ##################### Helpers ######################
/**
* #param {object} error
* #param {string} msg
*/
const renderError = (error, msg) =>
error ? <Alert severity="error">{msg}</Alert> : "";
// ############## Schema & Default Values ##############
const schema = yup.object().shape({
email: yup.string().required(),
password: yup.string().required(),
});
const defaultValues = {
email: "",
password: "",
};
// ###################### Styles ######################
const useStyles = makeStyles((theme) => ({
form: {
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1),
},
submit: {
margin: theme.spacing(3, 0, 2),
},
}));
// ################# Main Component ###################
const SignInPage = () => {
const classes = useStyles();
const {
handleSubmit,
formState: { errors },
reset,
control,
} = useForm({
mode: "all",
resolver: yupResolver(schema),
defaultValues,
});
const onSubmit = (data) => {
console.log(data);
// Reset form fields
reset();
};
console.log(errors);
const Form = () => (
<>
<Typography component="h1" variant="h5">
Sign In
</Typography>
<form className={classes.form} onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="email"
render={({ field }) => (
<TextField
variant="outlined"
margin="normal"
fullWidth
autoComplete="email"
label="Email Address"
{...field}
error={errors.email?.message ? true : false}
/>
)}
/>
{renderError(errors.email?.message, _MESSAGES.required)}
<Controller
control={control}
defaultValue=""
name="password"
render={({ field }) => (
<TextField
variant="outlined"
margin="normal"
fullWidth
autoComplete="current-password"
type="password"
label="Password"
{...field}
error={errors.password?.message ? true : false}
/>
)}
/>
{renderError(errors.password?.message, _MESSAGES.required)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
<Grid container>
<Grid item xs={12}>
<Link href="#" variant="body2">
Forgot password?
</Link>
</Grid>
<Grid item>
<Link href="#" variant="body2" color="secondary">
{"Don't have an account? Sign Up"}
</Link>
</Grid>
</Grid>
</form>
</>
);
return <AuthContainer Form={<Form />} />;
};
export default SignInPage;
What might have gone wrong?
Here is a Gif of what happened:
selected the input field
started typing
after one char it leaves the input field so, I need to select it to type again.
Move your Form component to its own Component, otherwise, you are mount and unmount your Form Component each re-render.
const App = () => {
const Form = () => {} // mount and unmount with each re-render
return <div><Form /></div>
}
to
const Form = () => {}
const App = () => {
return <div><Form /></div>
}
You have to pass down the ref. For MUI TextField should do the following:
<Controller
control={methods.control}
name="fieldName"
render={({ field: { ref, ...field } }) => (
<TextField {...field} inputRef={ref} /> // ⬅️ The ref
)}
/>
A working demo: https://codesandbox.io/s/broken-microservice-xpuru
Does it work now ?
This is SignIn Component. I am using Firebase concept. Material UI for designing purpose. I am using functional component.
Is it proper way to authenticate the user?I facing an error.
import React, { useState } from "react";
import Avatar from "#material-ui/core/Avatar";
import Button from "#material-ui/core/Button";
import CssBaseline from "#material-ui/core/CssBaseline";
import TextField from "#material-ui/core/TextField";
import LockOutlinedIcon from "#material-ui/icons/LockOutlined";
import Typography from "#material-ui/core/Typography";
import { makeStyles } from "#material-ui/core/styles";
import Container from "#material-ui/core/Container";
import Firebase from "../services/Firebase";
const Signin = () => {
const [values, setValues] = useState({email: "",password: ""})
const useStyles = makeStyles(theme => ({
paper: {
marginTop: theme.spacing(8),
display: "flex",
flexDirection: "column",
alignItems: "center"
},
avatar: {
margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main
},
form: {
width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1)
},
submit: {
height: 48,
padding: "0 15px",
margin: theme.spacing(7)
}
}));
const classes = useStyles();
const signin = (e) => {
e.preventDefault();
Firebase.auth().signInWithEmailAndPassword(values.email, values.password).then((u) => {
console.log(u)
}).catch((err) => {
console.log(err);
})
}
const signup = (e) => {
e.preventDefault();
Firebase.auth().createUserWithEmailAndPassword(values.email, values.password).then((u) => {
console.log(u)
}).catch((err) => {
console.log(err);
})
}
const handleChange = (e) => {
setValues({
[e.target.name]: e.target.value
})
}
return (
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Avatar className={classes.avatar}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Sign in
</Typography>
<form className={classes.form} noValidate>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
placeholder="Enter Email.."
onChange={(e) => handleChange(e)}
value={values.email}
/>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
placeholder="Enter Password.."
type="password"
onChange={(e) => handleChange(e)}
value={values.password}
/>
<Button
type="submit"
margin="normal"
variant="contained"
color="primary"
className={classes.submit}
onClick={(e) => signin(e)}
>
Sign In
</Button>
<Button
type="submit"
margin="normal"
variant="contained"
color="primary"
className={classes.submit}
onClick={(e) => signup(e)}
>
Sign Up
</Button>
</form>
</div>
</Container>
)
}
export default Signin;
This Firebase Component
import firebase from 'firebase';
require('firebase/app')
require('firebase/auth')
const Firebase = firebase.initializeApp({
apiKey: "AIzaSyC_3KRb7H0Xw1-DGfqAzqfxeZaw3W5PaLg",
authDomain: "my-login-page-react.firebaseapp.com",
databaseURL: "https://my-login-page-react.firebaseio.com",
projectId: "my-login-page-react",
storageBucket: "my-login-page-react.appspot.com",
messagingSenderId: "415587749418",
appId: "1:415587749418:web:ee026252bc0a64c1a57d53"
});
export default Firebase;
This will delete the initial object key-pairs making the value={values.*} in TextField uncontrolled.
setValues({
[e.target.name]: e.target.value
})
To override keeping earlier object key pairs use spread operation -
setValues({...values,
[e.target.name]: e.target.value
})
Problem might be caused by the way you keep values. You handle 2 values with 1 hook. When you call
setValues({
[e.target.name]: e.target.value
})
It probably overrides the previous values which had 2 values with 1 value, either e-mail or password so one of them becomes unidentified and
//This is an uncontrolled component
<TextField
variant="outlined"
margin="normal"
required
fullWidth
placeholder="Enter Email.."
onChange={(e) => handleChange(e)}
value={unidentified}
/>
Try to seperate your values as:
[email,setEmail] : useState("");
[password,setPassword] : useState("")