How to clear the controlled inputs in ReactJS? - javascript

this is a sign-in page I created using React & styled components. I was trying to clear the input field values "onSubmit" but for some issue it does not work. Does anyone knows how should I clear the inputs with the button click ? Thank you!
import React, { useState } from "react";
import {
Container,
Form,
FormButton,
FormContent,
FormH1,
FormInput,
FormLabel,
FormWrap,
Icon,
Text,
} from "./SigninElements";
const SignIn = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handlePasswordChange = (e) => {
setPassword(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(email, password);
setEmail("");
setPassword("");
};
return (
<Container>
<FormWrap>
<Icon to="/">Sushi Finder</Icon>
<FormContent>
<Form action="POST" onSubmit={handleSubmit}>
<FormH1>Sign in to your account</FormH1>
<FormLabel htmlFor="for">Email</FormLabel>
<FormInput type="email" required onChange={handleEmailChange} />
<FormLabel htmlFor="for">Password</FormLabel>
<FormInput
type="password"
required
onChange={handlePasswordChange}
/>
<FormButton type="submit">Continue</FormButton>
<Text>Forgot Password</Text>
</Form>
</FormContent>
</FormWrap>
</Container>
);
};
export default SignIn;

What you have are not controlled inputs, as Jayce444 points out. If you want to clear the inputs then you can do 1 of 2 things:
Actually make your inputs controlled by setting the value prop accordingly. Then the state updates in the submit handler will clear the state and be reflected in the inputs' values.
<FormInput
type="email"
required
onChange={handleEmailChange}
value={email}
/>
...
<FormInput
type="password"
required
onChange={handlePasswordChange}
value={password}
/>
Leave them uncontrolled and clear the inputs via the onSubmit event in the handler. Provide an id for each input to access in the submit handler and reset their values.
const handleSubmit = (e) => {
e.preventDefault();
console.log(email, password);
setEmail("");
setPassword("");
e.target.email.value = "";
e.target.password.value = "";
};
...
<FormInput
type="email"
required
onChange={handleEmailChange}
id="email"
/>
...
<FormInput
type="password"
required
onChange={handlePasswordChange}
id="password"
/>

Its a uncontrolled component. That means single way approach you pass the value from input. But not possible to change the input via Dom.
So better use controlled component value={email}. So this will reset
the value via state.
And i have post solution for this uncontrolled component via key updated method.
Example Codesanbox
import React, { useState } from "react";
import {
Container,
Form,
FormButton,
FormContent,
FormH1,
FormInput,
FormLabel,
FormWrap,
Icon,
Text,
} from "./SigninElements";
const keyGen = () => 'key'+Math.round(Math.random()*10000000000000);
const SignIn = () => {
const [key, setKey] = useState(keyGen());
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleEmailChange = (e) => {
setEmail(e.target.value);
};
const handlePasswordChange = (e) => {
setPassword(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(email, password);
setEmail("");
setPassword("");
setKey(keyGen())
};
return (
<Container>
<FormWrap>
<Icon to="/">Sushi Finder</Icon>
<FormContent key={key}>
<Form action="POST" onSubmit={handleSubmit}>
<FormH1>Sign in to your account</FormH1>
<FormLabel htmlFor="for">Email</FormLabel>
<FormInput type="email" required onChange={handleEmailChange} />
<FormLabel htmlFor="for">Password</FormLabel>
<FormInput
type="password"
required
onChange={handlePasswordChange}
/>
<FormButton type="submit">Continue</FormButton>
<Text>Forgot Password</Text>
</Form>
</FormContent>
</FormWrap>
</Container>
);
};
export default SignIn;

You can also use useRef() to control those input elements, which includes clearing them. Declare them for each input like so:
const input1 = React.useRef();
const input2 = React.useRef();
Then use the ref prop of the FormInput:
<FormInput
ref={input1}
type="password"
required
onChange={handlePasswordChange}
id="password"
/>
And then you can clear them in your handleSubmit function like so:
input1.current.value = ""

The beauty of controlled components are that you can control them outside of the DOM. All you simply need to do is set the value state variable to null.
const [myValue, setMyValue] = React.useState(null)
const clearValue = () => {
setMyValue(null)
}
const handleOnChange = (event) => {
setMyValue(event.target.value)
}
return <>
<FormInput
onChange={handleOnChange}
value={myValue}
/>
<Button onClick={clearValue}>Clear</Button>
</>
Since the clear button calls clearValue on click and the clearValue sets myValue to null on click, this should clear the value of your input. Controlled components are driven by a variable, which is updated by what the user types. Uncontrolled components are only driven by what the user types.
To use a controlled component you are required to pass your state variable back to the component though.

Related

Only displaying input value after button click in React

I am trying to create a simple React form that, on button click, will display the entered input value in a controlled input element. Specifically, I do NOT want to have an identical solution to that in the React docs (constantly updating the displayed value on input change), rather I only want it to update the displayed paragraph text after the user has hit the submit button. I am able to do this with this current solution (conditionally rendering based on submitted state that is set in handler functions):
import { useState } from 'react';
export default function App() {
const [text, setText] = useState('');
const [submitted, setSubmitted] = useState(false);
const handleSubmit = e => {
e.preventDefault();
setSubmitted(true);
};
const handleChange = e => {
setSubmitted(false);
setText(e.target.value);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<label>Text: </label>
<input type="text" value={text} onChange={e => handleChange(e)} />
<button type="submit" onClick={handleSubmit}>
Show
</button>
{submitted && <p>{text}</p>}
</form>
</>
);
}
But I am guessing that there is a much better way to do this.
If you want to be able to submit multiple times and keep the last before submitting again, use this:
import { useState } from 'react';
export default function App() {
const [text, setText] = useState('');
const [displayText, setDisplayText] = useState('');
const handleSubmit = e => {
e.preventDefault();
setDisplayText(text);
};
const handleChange = e => {
setText(e.target.value);
};
return (
<>
<form onSubmit={e => handleSubmit(e)}>
<label>Text: </label>
<input type="text" value={text} onChange={e => handleChange(e)} />
<button type="submit" onClick={handleSubmit}>
Show
</button>
{displayText && <p>{displayText}</p>}
</form>
</>
);
}
displayText && ... is so that the paragraph tag doesn't exist until it has a value to display, but you can just replace that section with out that and it will work.

How to change TexField variant on input focus and maintain the focus using Material-ui

When the user focuses on input I want to change the variant on the TextField. Bellow snippet does that, but the input loses focus. you need to click again on input in order to focus and write some text inside
import React, { useState } from 'react'
import { TextField } from '#material-ui/core'
const App = () => {
const [name, setName] = useState('')
const [focus, setFocus] = useState(false)
return <TextField
variant={focus ? 'outlined' : 'standard'}
onFocus={(e) => setFocus(true)}
value={name}
name='name'
onChange={(e) => setName(e.target.value)} />
}
sandbox: https://codesandbox.io/s/material-ui-dynamic-variant-7up6q?file=/demo.tsx
My understanding is the following:
TextField component re-renders with new props and is creating a new input element to display while destroying the old one. In this way, the user needs to do 2 clicks on input before texting.
I tried with onClick also, leading to the same result.
Is there a way to obtain these results without losing the focus on input?
Use "useRef" for focusing the input, inputRef prop will help you to set ref. And useEffect to track and update.
const App = () => {
const [name, setName] = useState("");
const [focus, setFocus] = useState(false);
const inputRef = useRef(null);
const onFocus = () => {
setFocus(true);
};
const onBlur = () => {
setFocus(false);
};
useEffect(() => {
if (focus) {
inputRef.current.focus();
}
}, [focus]);
return (
<TextField
variant={focus ? "outlined" : "standard"}
onFocus={onFocus}
onBlur={onBlur}
value={name}
inputRef={inputRef}
name="name"
onChange={(e) => setName(e.target.value)}
/>
);
};
export default App;
You can check demo here:
Link - https://codesandbox.io/s/material-ui-dynamic-variant-forked-8pbdi?file=/demo.tsx

How to test form submit with jest and enzyme in react?

I am learning reactjs form with hooks, now I would like to test form on submit using jest and enzyme.
here is my login component.
import React from 'react'
function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
// ....api calLS
}
return (
<div>
<form onSubmit={handleSubmit} className="login">
<input type="email" id="email-input" name="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" id="password-input" name="password" value={password} onChange={e =>setPassword(e.target.value)} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
export default Login
Here is the login.test.js file
describe('my sweet test', () => {
it('clicks it', () => {
const wrapper = shallow(<Login />);
const updatedEmailInput = simulateChangeOnInput(wrapper, 'input#email-input', 'blah#gmail.com')
const updatedPasswordInput = simulateChangeOnInput(wrapper, 'input#password-input', 'death');
expect(updatedEmailInput.props().value).toEqual('blah#gmail.com');
expect(updatedPasswordInput.props().value).toEqual('death');
const instance = wrapper.instance()
const spy = jest.spyOn(instance, 'handleSubmit')
instance.forceUpdate();
const submitBtn = app.find('#sign-in')
submitBtn.simulate('click')
expect(spy).toHaveBeenCalled()
})
})
Unfortunately when I run npm test I get the following error.
What do I need to do to solve this error or can someone provide a tutorial on how to test a form submit?
In the documentation it's said that you cant use shallow.instance() for functional components
It will return null: https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/instance.html
There was also a previous answer on this topik
Enzyme instance() returns null
You can pass validated function handleSubmit to Login as a prop like there How to use jest.spyOn with React function component using Typescript
// Unit test
describe('SomeComponent' () => {
it('validates model on button click', () => {
const handleSubmit = jest.fn();
const wrapper = mount(
<Login handleSubmit={handleSubmit}/>
);
const instance = wrapper.instance();
const submitBtn = app.find('#sign-in')
submitBtn.simulate('click')
expect(handleSubmit).toHaveBeenCalled();
});
}
You need to call this test function handleSubmit in your login component either as a part of onSubmit or export whole onSubmit from upper components. Example login code with importing part of login function
import React from 'react'
function Login( {handleSubmit}) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const onSubmit = async (e) => {
if (handleSubmit) {
handleSubmit()
}
e.preventDefault();
// ....api calLS
}
return (
<div>
<form onSubmit={onSubmit} className="login">
<input type="email" id="email-input" name="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" id="password-input" name="password" value={password} onChange={e =>setPassword(e.target.value)} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
export default Login
Example login code with importing of submit function
import React from 'react'
function Login( {handleSubmit}) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
// handleSubmit is imported with props
return (
<div>
<form onSubmit={handleSubmit} className="login">
<input type="email" id="email-input" name="email" value={email} onChange={e => setEmail(e.target.value)} />
<input type="password" id="password-input" name="password" value={password} onChange={e =>setPassword(e.target.value)} />
<input type="submit" value="Submit" />
</form>
</div>
)
}
export default Login

How to disable a button if the user entered input is not valid email using javascript and react?

I want to add email validation to the input field and based on that disable the add button if the email entered by user is wrong.
below you can see my code,
function Parent() {
const [email, setEmail] = useState('');
const onEmailChange = (event: any) => {
setEmail(event.target.value);
};
const isDisabled = email.length === 0;
return (
<Input
type="email"
value={email}
onChange={onEmailChange}
placeholder="Insert user email"
/>
<button disabled={isdisabled}>Add</button> //add button to be disabled when user input email is wrong
);
}
I want to make sure i have basic email validation for the input and be able to enter only numbers, ''.
Could someone help me with this? Thanks in advance.
EDIT:
image for the error unnecessary escape character
There are multiple ways of doing this but I would advise keeping a track of the disabled state of the button in its own state which is initialized to true.
Now change the disabled state inside a useEffect which runs every time the email is changed and set it to true or false based on your validation.
import React from "react";
// Modify this function as per your needs
const validateEmail = email => typeof email === "string" && email.includes("#");
export default function App() {
const [email, setEmail] = React.useState("");
const [isDisabled, setIsDisabled] = React.useState(true);
const onEmailChange = event => {
setEmail(event.target.value);
};
React.useEffect(() => {
setIsDisabled(!validateEmail(email));
}, [email]);
return (
<>
<input
type="text"
value={email}
onChange={onEmailChange}
placeholder="Insert user email"
/>
<button disabled={isDisabled}>Add</button>
</>
);
}
Here is the working prototype in codesandbox
You can use regex in order to check if the input value is an email by using the onChange() property.
import React from "react";
const regex = /^(([^<>()[\].,;:\s#"]+(\.[^<>()[\].,;:\s#"]+)*)|(".+"))#(([^<>()[\].,;:\s#"]+\.)+[^<>()[\].,;:\s#"]{2,})$/i;
export default function App() {
const [isDisabled, setDisibility] = React.useState(true);
const checkEmail = e => {
setDisibility(!regex.test(e.target.value));
}
return (
<div>
<input onChange={checkEmail} placeholder="email address" />
<button disabled={isDisabled}>add</button>
</div>
);
}
https://codesandbox.io/s/dry-sun-votmt?fontsize=14&hidenavigation=1&theme=dark

How to reset my input field after I submit form using pure React.js Hooks, no libraries

I am trying to reset my input field after I submit the form using only React.js, using hooks from React itself.
Here is my code:
import React, { useState } from 'react';
const Searchbar = (props) => {
const { updateSelectedCity } = props;
const [newCity, setCity] = useState("");
const handleChange = (e) => {
setCity(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
updateSelectedCity(newCity);
console.log(newCity);
}
return (
<div>
<form onSubmit={handleSubmit}>
<input name="cityName" placeholder={placeholder} onChange={handleChange} />
<button type="submit">Check Weather</button>
</form>
</div>
)
};
export default Searchbar;
What do I need to add in order to accomplish that? Do I need to use useEffect? Or is there any way I can do it without using external libraries?
Thanks for the help!
Set the <input> to get its value from state:
<input name="cityName" placeholder={placeholder} onChange={handleChange} value={newCity} />
And just update state after handling the submit event:
const handleSubmit = (e) => {
e.preventDefault();
updateSelectedCity(newCity);
console.log(newCity);
setCity(""); // <--- here
}

Categories

Resources