In my React component, I need to listen for a value to change then trigger a form to be submitted.
Currently I'm using a useEffect hook to listen for changes to a state variable mediaBlob,
import { useEffect, useState } from "react";
export function Foo({
handleSubmit,
...props
}) {
let { mediaBlob, setMediaBlob } = useState(null)
useEffect(() => {
if (mediaBlob) {
// how to trigger the form submission here?
// Cant call the form's handleSubmit() method without an event
}
}, [mediaBlob]);
but I cant call the form's handleSubmit function because the function needs to accept an event object e.
async function handleSubmit(e) {
e.preventDefault()
// ...
}
return (
<form onSubmit={handleSubmit}>
<Foo handleSubmit={handleSubmit} />
</form>
)
Is it still possible to trigger the form submission from within the useEffect hook?
Extract code after e.preventDefault() into another function and call that from useEffect. In case you need to get form Data from the event, you also need to use ref to get reference to form element and extract needed data. You can also use controlled inputs.
Yes, It's possible to trigger form submission form within useEffect using useRef here is sample code:
import { useEffect, useState, useRef } from "react";
export function Foo({
handleSubmit,
...props
}) {
const { mediaBlob, setMediaBlob } = useState(null)
const ref = useRef(null);
useEffect(() => {
if (mediaBlob) {
ref.current.submit();
}
}, [mediaBlob]);
return (
<form onSubmit={handleSubmit} ref={ref}>
{/* form content */}
</form>
)
}
return (
<form onSubmit={handleSubmit}>
<Foo handleSubmit={handleSubmit} />
</form>
)
Related
//use Input HOOK
I want to know that how this custom hook work
import { useState } from "react";
export default initialValue => {
const [value, setValue] = useState(initialValue);
return {
value,
onChange: event => {
setValue(event.target.value);
},
reset: () => setValue("")
};
};
//todo form
How this onchange method work how it update the data even though no onchange function is write in this programm
import React from "react";
import TextField from "#material-ui/core/TextField";
import useInputState from "./useInputState";
const TodoForm = ({ saveTodo }) => {
const { value, reset, onChange } = useInputState("");
return (
<form
onSubmit={event => {
event.preventDefault();
saveTodo(value);
reset();
}}
>
<TextField
variant="outlined"
placeholder="Add todo"
margin="normal"
value={value}
onChange={onChange}
/>
</form>
);
};
export default TodoForm;
view full programm Code Sandbox link
Functions in JS are treated like any other variable. So the de-structured onChange (in the component) is taking the reference for a function which is defined anonymously in the custom hook, which is then used by the onChange method of the TextField component.
This is similar to how you pass variables by reference is JS.
I'm building a basic custom component for input masking (rolling my own!), which returns an input field for a form.
import React, { useState, useEffect, useRef } from "react";
import mask from "./mask";
const InputMask = props => {
let {
field,
register,
type,
inputMaskType,
defaultValue
} = props;
const inputField = useRef();
const [fieldValue, setFieldValue] = useState("");
const onInputLoad = () => {
setFieldValue(mask.maskShortDateOnLoad({
inputField,
defaultValue
}));
};
const onInputChange = () => {
setFieldValue(mask.maskInput({
type: inputMaskType,
inputField,
defaultValue
}));
};
useEffect(() => {
onInputLoad();
}, []);
return (
<>
<input
{...register(field)}
ref={inputField}
type={type}
onChange={onInputChange}
value={fieldValue || ""}
/>
</>
);
};
export default InputMask;
Used like so, in the parent component, in the form:
<InputMask
type="text"
field="from_date"
register={register}
inputMaskType={inputMaskType.SHORT_DATE}
defaultValue={selectedEntity.from_date}
/>
It's binding to the field when the form loads, because the registered field is reading the data in. It shows up. On save, however, the custom field's data is not updated. It remains the same as it was when first loaded.
Looking at it in dev tools, it has the correct "name" property on the tag, and it looks like all the others in the form.
I guess I don't get it! It's an input, nested in another component, and it's registered like every other input in the form. I'm passing register down and doing what I'm supposed to. Is the state change reloading w/ the old data? Am I going to be forced to use a Controller? If so...how in this case? What am I doing wrong?
I have semi-legacy code. There's some Formik components that are being wrapped with withFormik().
The primary thing is, the variable/data is not part of the initial props of the wrapped component. A possible example is if React Context API is being used. That means it isn't part of the props. Quick simple React component and withFormik() below with React's useContext hook being used in the wrapped component, but would like access to it in the withFormik() HOC.
Also, the data can't be passed through [hidden] fields in the form. I realize that is an easy way to do it and grab it in values. Wondering if there is another way.
import React, { useContext } from 'react';
import { Form, Field, withFormik } from 'formik';
const MyForm = props => {
const { section } = useContext( SectionContext );
return (
<Form>
<Field
component="input"
name="name"
/>
{errors.name && touched.name && <div id="feedback">{errors.name}</div>}
<button type="submit">Submit</button>
</form>
);
};
const MyEnhancedForm = withFormik({
mapPropsToValues: () => ({ name: '' }),
handleSubmit: (values, { setSubmitting, props }) => {
// Want to use the context, section.
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}
})(MyForm);
Thanks for any help
I have been trying to learn React-query but can't seem to trigger requests with my onSubmit event. Right now the code is sending the request with "washington" as the default parameter and printing it to the screen, and a new request also triggers with the onBlur event, and fetch the data if the city typed is valid.
The thing is that wish I could move this logic to the submit() function, treat the data on the input and only if the data is valid, proceed to make the request. This is the stackblitz where I reproduced the problem with a free apiKey: StackBlitz
This is the code:
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const Fetch = async city => {
let apiKey = '91b5ff77e9e7d1985a6c80bbbb3b2034';
const { data } = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`
);
return data;
};
const Weather = () => {
const [city, setCity] = useState('washington');
const { data, error } = useQuery(['temperature', city], () => Fetch(city));
const submit = () => {};
return (
<div>
<form onSubmit={submit}>
<input onBlur={e => setCity(e.target.value)} type="text" />
<button type="submit">send</button>
</form>
{!data ? null : <div>{data.main.temp}</div>}
</div>
);
};
export default Weather;
You can also call setCity in the onSubmit event of the form, as the onSubmit event gets the complete submitted form in the submit event:
<form
onSubmit={(event) => {
event.preventDefault();
const city = new FormData(event.currentTarget).get("city");
// do validation here
if (isValid(city)) {
setCity(city)
}
>
<input name="city" type="text" />
<button type="submit">send</button>
</form>
make sure to give your input a name so that you can grab it from the form submit event.
You can use useMutation hooks. As what the documentation said mutations are typically used to create/update/delete data or perform server side-effects. For this purpose, React Query exports a useMutation hook.. This hooks will return an object that gives you a mutation function that you can use to trigger request based on user interactions.
const { mutate: renamedMutationFunction } = useMutation(newTodo => axios.post('/todos', newTodo)).
Then somewhere in your code, you can do:
const handleClick = () => { renamedMutationFunction(); //invoking the mutation }
EDIT
see #TkDodo answer for better solution. You can basically just re-set the city, and react-query will automatically refetch the data.
I've been studying react and developing an app, but i got a problem using context. In one component I create the context and provide its value, but when I try to use the current value of context in another component, I have the default value. Code:
Component One:
export const OwnerInformationContext = React.createContext({})
function NameChoose() {
...
const [ownerInformation,setOwnerInformation] = useState({})
function onpressSubmitButton(e : FormEvent) {
e.preventDefault();
...
setOwnerInformation({name:'name',roomId:'id',owner:'true'})
}
return(
<div className="page-container">
<OwnerInformationContext.Provider value={ownerInformation} />
...
<form onSubmit={onpressSubmitButton}>
...
</form>
...
);
}
export default NameChoose;
So when i try to use by:
import { OwnerInformationContext } from '../NameChoose/index'
function ComponentTwo(){
const consumeOwnerContext = useContext(OwnerInformationContext)
useEffect(() => {
console.log(consumeOwnerContext)
}, [])
return <h1>test</h1>
}
I got the default value provide in component one, that's {}.
It looks like your context provider is not actually wrapping any components, as it has a self-closing tag:
<OwnerInformationContext.Provider value={ownerInformation} />
It should be:
<OwnerInformationContext.Provider value={ownerInformation}>
{/* Your child components here will have access to the context */}
</OwnerInformationContext.Provider>
You are using useEffect as ComponentDidMount meaning only at start(mount) the value will be console log.
you should give consumeOwnerContext as a dependency to useEffect like this
useEffect(()=>{
console.log(consumeOwnerContext);
},[consumeOwnerContext]);
And rename consumeOwnerContext to consumeOwnerValue, because you are getting the value out of the context using useContext.
After that when you will click on submit button you should have ComponentTwo console log it.
import React, { useState, useEffect, useContext } from "react";
export const OwnerInformationContext = React.createContext({});
function ComponentTwo() {
const consumeOwnerContext = useContext(OwnerInformationContext);
useEffect(() => {
// You are using consumeOwnerContext inside useEffect, in that case add
// it as dependency if you want to see the updated consumeOwnerContext value
console.log(consumeOwnerContext);
}, [consumeOwnerContext]);
return <div>test</div>;
};
function NameChoose() {
const [ownerInformation, setOwnerInformation] = useState({});
function onpressSubmitButton(e) {
e.preventDefault();
setOwnerInformation({ name: "name",roomId: "id",owner: "true",});
}
return (
// The 'OwnerInformationContext.Provider' has to wrap the component
// that will use its context value. In your case, ComponentTwo
// has to be a child of NameChoose.
<OwnerInformationContext.Provider value={ownerInformation}>
<div className="page-container">
<form onSubmit={onpressSubmitButton}>
<button type="submit">Submit</button>
</form>
</div>
<ComponentTwo />
</OwnerInformationContext.Provider>
);
}
export default NameChoose;