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.
Related
I have a form that have an onSubmit, in that callback I have a uploady.showFileUpload(), but the code after uploady.showFileUpload() is executed.
Now the question is how can I wait for uploady, and then execute the rest of the code?
const handleSubmit2 = useCallback((e)=> {
uploady.showFileUpload(); //(HERE SHOULD WAIT FOR IT TO FINISH FILE SELECT)
//OTHER CODE
});
This codesandbox should be helpful in the case of using Uploady with a form:
https://codesandbox.io/s/react-uploady-inside-form-ys1wx
The idea is that you show the file selection prompt separately from submitting the form:
import React, { useState, useCallback, useMemo, forwardRef } from "react";
import Uploady, {
useUploadyContext
} from "#rpldy/uploady";
import { asUploadButton } from "#rpldy/upload-button";
const MyUploadField = asUploadButton(
forwardRef(({ onChange, ...props }, ref) => {
return (
<div {...props} ref={ref} id="form-upload-button" title={text}>
Select file
</div>
);
})
);
const MyForm = () => {
const [fields, setFields] = useState({});
const [fileName, setFileName] = useState(null);
const uploadyContext = useUploadyContext();
const onSubmit = useCallback(() => {
uploadyContext.processPending({ params: fields });
}, [fields, uploadyContext]);
const onFieldChange = useCallback(
(e) => {
setFields({
...fields,
[e.currentTarget.id]: e.currentTarget.value
});
},
[fields, setFields]
);
return (
<form>
<MyUploadField autoUpload={false} />
<input
onChange={onFieldChange}
id="field-name"
type="text"
placeholder="your name"
/>
<SubmitButton
id="form-submit"
type="button"
onClick={onSubmit}
>
Submit Form
</SubmitButton>
</form>
);
};
<Uploady
clearPendingOnAdd
destination={{ url: "[upload-url]" }}
multiple={false}
>
<MyForm />
</Uploady>
Selection is achieved by using the asUploadButton HOC. You can of course do so yourself as you did with uploady.showFileUpload();.
Then, the submit button uses uploady's processPending method to start uploading.
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
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.
Hello Guys This is how my code looks like
export default function Billing() {
const classes = useStyles();
const [balance, setBalance] = useState('0.00');
const [upcoming, setUpcoming] = useState('0.00');
const [lastmonth, setLastmonth] = useState('0.00');
const [header, setHeader] = useState('Please Enter The Amount');
const [open, setOpen] = useState(false);
const [amountstate, setAmountstate] = useState(false);
const [amounthelper, setAmounthelper] = useState('');
const [sairam, setSairam] = useState(0);
const onchange = async (e) => {
console.log(sairam)
setSairam({amount: e.target.value})
}
const [modalchild, setModalchild] = useState(<>
<form noValidate autoComplete="off">
<TextField
error={amountstate}
type="number"
value={sairam}
onChange={onchange}
label='Please enter the amount'
helperText={amounthelper}
variant="outlined"
/>
<br />
<br />
<Button variant="contained" color="primary" onClick={() => addBalance()}>Add Balance</Button>
</form>
</>);
const [country, setCountry] = useState(false);
const [currency, setCurrency] = useState('');
const addBalance = () => {
console.log("Clicked :)")
console.log(sairam) // outputs the inital value not the changed value
});
return(<>
<div className="balance-container">
<div className="columns-top">
<div className="column-top">
<h1>${upcoming}</h1>
</div>
<div className="column-top">
<h1>${balance}</h1>
<Button variant="contained" color="primary" onClick={handleOpen}>Add Balance</Button>
</div>
<div className="column-top">
<h1>${lastmonth}</h1>
</div>
</div>
</div>
<Modal
aria-labelledby="transition-modal-title"
aria-describedby="transition-modal-description"
className={classes.modal}
open={open}
onClose={handleClose}
closeAfterTransition
BackdropComponent={Backdrop}
BackdropProps={{
timeout: 500,
}}
>
<Fade in={open}>
<div className={classes.paper}>
<h2 id="transition-modal-title">{header}</h2>
{modalchild}
</div>
</Fade>
</Modal>
</>
)
}
if I change the state sairam by using the function setSairam() the state is not getting updated and its only logging the initial state which is 0 i have even tried the loggint the event value of my input it works correct but the state alone is not changing please help me feel free to give me any other options
setSairam() is the asynchronous method so you can't get the updated value of sairam immediately after setSairam().
const onchange = async (e) => {
setSairam({amount: e.target.value})
console.log(sairam) // This will console old value of sairam
}
You should use useEffect with adding a sairam dependency to check the updated state value.
useEffect(() => {
console.log(sairam)
}, [sairam]);
IMPORTANT
sairam will be the value of the TextField, so you should update the sairam to the string value, not object.
setSairam(e.target.value)
You will not get the updated value in your onChange function because that function is not aware when state is changed.
Use useCallback to get the updated value of sairam. Pass sairam in the dependency array.
const onchange = useCallback(async (e) => {
console.log(sairam)
setSairam({amount: e.target.value})
},[sairam]);
or use prevState
setSairam(prevState => {
console.log(sairam)
return ({amount: e.target.value})
});
Another option is to pass sairam to onChange function. But I am not sure how it works since your markup is stored in state
onChange={(e)=>onchange(e, sairam)}
const onchange = async (e, sairam) => {
console.log(sairam)
setSairam({amount: e.target.value})
}
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
}