Prevent function from running when a variable changes in React - reactjs

I have a form with a checkbox and text input.
const [formVars, setFormVars] = useState<any>({checkbox:true})
const submitForm = () => {
console.log({formVars})
}
render (
<Checkbox checked={formVars.checkbox} /> Bake the cookies?
<TextInput label="what kind of cookies?" />
<Button onClick={() => submitForm()}
)
submitForm should not run until the user clicks submit, however my code seems to be producing a never-ending loop where the form changes formVars, and the change sets off submitForm. How can I prevent this?

There are a bunch of typos in the question, so I created a simple copy of your code.
The button's onClick prop takes a callback, not a function call. You're likely calling the submit function instead of passing it to the button onClick prop.
Here's how you trigger the submission manually for controlled inputs
export default function App() {
const [formVars, setFormVars] = useState({ checkbox: true });
const submitForm = (e) => {
console.log({ formVars });
};
const handleChange = (e) => {
setFormVars(e.target.value);
};
return (
<>
<label>
Name:
<input onChange={handleChange} type="text" name="name" />
</label>
<button onClick={submitForm}>submit</button>
</>
);
}

<Button onClick={()=> submitForm}
Try this syntax for the button

Related

value of TextField input from event target in react with typescript

I'm trying to console log the value of the TextField input in material ui while using typescript in react. But I got undefined in the console. I tried several solutions, but none worked.
Here's my code:
const HomePage: React.FC = () => {
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log((e.currentTarget as HTMLInputElement).value);
};
return (
<Container maxWidth="xl">
<div style={{width: '385px', margin: 'auto'}}>
<form onSubmit={(e) => {onSubmit(e)}}>
<Stack direction='row' spacing={1}>
<CustomTextField placeholder="Enter name" label="Name" type="text" variant="outlined" name="name" />
<Button type="submit" variant="contained" size='large'>Add</Button>
</Stack>
</form>
</div>
</Container>
)
}
export default HomePage;
I think you may have conflated two examples on dealing with input values to get an incorrect solution.
Because the onSubmit handler is attached to the form, it will have access to all the input values within that form within event.target.elements[..].value, of which there could be many. You can then select the value you want using the name attribute of the input, in this case you have set it to name="name" so your onSubmit handler can access it as such:
const onSubmit = (e: React.FormEvent) => {
e.preventDefault();
console.log(e.target.elements.name.value);
};
Edit: you could also access the input directly by dropping the .elements, so e.target.name.value would also work.
What I think you may have confused this with is an onChange handler attached to an input, such as <CustomTextField />, where because there is only one input the event object is able to hold the value at event.target.value.
I managed to get the value by using the following:
const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const name = (e.currentTarget.elements.namedItem('name') as HTMLInputElement).value;
};

formik how to trigger submit from outside

I got a form like this
const SubtitleForm = ({ sub }) => {
const formik: any = useFormik({
initialValues: sub,
onSubmit: async (values) => {
const res = await update(values)
},
})
return (
<form className="subtitleFormComponent">
<input
className="subtitle"
autoComplete="off"
type="text"
name="name"
onChange={formik.handleChange}
value={formik.values.name}
/>
</form>
)
}
export default SubtitleForm
How can I trigger submit from outside this form / component ?
If you meant to have the submit button in a different component to the fields; I'd suggest to wrap both in the form. Then, you can use the useFormikContext in the inputs to update the data, and in the submit button.
A POC of the idea:
https://codesandbox.io/s/sharp-chebyshev-5nonm?file=/src/App.js

How do I get input from user by using <button> and <input> in React?

I want to create a form by using <input> and <button> like so:
I'm not sure how to pass the value I get from the text input field into a couple of functions on the frontend when I press the button "Send now". How do I make a <form onSubmit={}> without using forms, rather just an input field and a button?
This is what I've tried so far and it's not working, I'm not sure how to get the input value and pass it into the function like this this.articleOnSubmit(*user input*). And also is the way i incorporated onChange={this.articleOnChange} correct? Here is my code:
const Input = ({ placeholder, name, type, value }) => (
<input
placeholder={placeholder}
type={type}
step="0.0001"
value={value}
name={value}
/>
);
class Publish extends Component {
constructor(props) {
super(props);
this.state = {
articleTitle: '',
};
}
articleOnChange = (event) => {
let title = event.target.value;
this.setState({ articleTitle: title.toLowerCase() });
}
articleOnSubmit = (event) => {
if (this.state.articleTitle) {
console.log(this.state.articleTitle);
this.hash(this.state.articleTitle)
.then(hex => {console.log(hex); return hex;})
.then(hex => this.addArticleHash(hex));
event.preventDefault();
}
return (
<Input placeholder="Article Name" name="article" type="text" onChange={this.articleOnChange} />
<div/>
{false
? (<Loader />)
: (
<button
type="submit"
onClick={this.articleOnSubmit(Input.name)}
>
Send now
</button>
)}
</div>
);
}
To make it work you have to fix 2 issues here:
Since Input is a component, you have to get in its props the onChange prop you pass to it, otherwise it won't work.
const Input = ({ placeholder, name, type, value, onChange }) => (
<input
onChange={onChange}
placeholder={placeholder}
type={type}
step="0.0001"
value={value}
name={value}
/>
);
React onClick should be a function. Not a call. A function. What you did here was onClick={this.articleOnSubmit(Input.name)} - which is a call.
The solution is to change it into a function () => this.articleOnSubmit(Input.name)

OnSubmit event not fired with preventDefault on the container

Would somebody explain to me why the submit callback is not called when I try to submit the form (using the button or by pressing enter) ?
The reason is this onClick event on the container but I cannot grasp why...
Shouldn't the submit event be caught by the form before bubbling up to the div with onClick?
export default function App() {
const [val, setVal] = useState("");
function submit() {
alert(val);
}
function prevent(e) {
e.preventDefault();
}
return (
<div className="App" onClick={prevent}>
<form onSubmit={submit}>
<input
name="test"
value={val}
onChange={(e) => setVal(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
codesandbox
Edit:
My form is inside an 'a' tag and because of that, I wanted to use preventDefault so the user to not be redirected when filling up the form.
Thanks in advance!
I am not sure what you are trying to achieve here, but preventing default actions on the parent element is not actually the best thing to do. Your code could have been better this way:
export default function App() {
const [val, setVal] = useState("");
function submit(e) {
e.preventDefault();
alert(val);
}
return (
<form onSubmit={(e) => submit(e)}>
<input
name="test"
value={val}
onChange={(e) => setVal(e.target.value)}
/>
<button type="submit">Submit</button>
</form>);
}
The prevent() function is unnecessary and have no meaningful impacts on your code. Keeping it simple and sweet is the best way to go.

React Formik : How to useEffect with formik values?

I would like to use useEffect to rerender a field if values of formik has changed. But it doesn't work..
Actually I created a little example to show my problem.
Here I have one field 'title', if this field has changed (when we write in it) it should call useEffect and print 'update!' but it doesn't!!
const FormikWidgetConfigurator = (props) => {
useEffect(() => {
// doesn't work if values has changed
console.log('update!')
}, [props.values]);
return (
<Form onSubmit={ props.handleSubmit } noValidate>
<Form.Group className='py-3' >
<Col md='6' style={{ padding: '0px' }}>
<Form.Group controlId='title'>
<Form.Control
type='text'
value={ props.values.title }
onChange={props.handleChange}
/>
</Form.Group>
</Col>
</Form.Group>
</Form>
)
}
const WidgetConfigurator = withFormik({
mapPropsToValues(props) {
return {
title: 'No Title'
};
},
validationSchema: props => Yup.object().shape({title: Yup.string()}),
handleSubmit(values, { setSubmitting }) {// handle submit}
})(FormikWidgetConfigurator);
export default WidgetConfigurator;
EDIT: Actually it works as expected. (i didn't change anything)
Thanks!
Using vanilla Formik your approach works.
My guess is that the issue is in your custom components, Form.Control, or Form.Group
It's not a great solution, but a hack I found is to have an invisible button inside a Formik form; it has access to all of Formik's attributes and will do whatever logic is needed in its onClick. Then from a useEffect you can simulate the click of that button via document.getElementById("..").click().
// Style it to be invisible
<Button id="testButton" type="button" onClick={() => {
setFieldValue('test', '123');
setTouched({});
// etc. any Formik action
}}>
</Button>
useEffect:
useEffect(() => {
document.getElementById("testButton").click(); // Simulate click
}, [dependency);

Resources