Data from API is not filled inputs fields. Formik - javascript

I create simple form on functional component ReactJS, with using Formik. Input fields my form should get data from API, when component mount. For this i'm using fetch and useEffect. After fetching, data from the API is retrieved but not filled until formik.handleChange is triggered.
If try to specify something in any input field, formik.handleChange is triggered, and all fields are immediately filled with data from the API (in my case, sould filled only email)
How i can triggered formik.handleChange when component will mount ?
import React, { useEffect } from 'react';
import { TextField, Button } from '#material-ui/core/';
import { useFormik } from 'formik';
import * as yup from "yup";
const validationSchema = yup.object({
Title: yup.string("Enter your Title").required("Title is required"),
email: yup
.string("Enter your email")
.email("Enter a valid email")
.required("Email is required"),
});
export default function Form() {
const formik = useFormik({
initialValues: {
Title: "",
email: ""
},
validationSchema: validationSchema,
onSubmit: async (values) => {
//...code of post function
//...code of post function
//...code of post function
}
});
useEffect(() => {
(async () => {
try {
let response = await fetch(
"https://run.mocky.io/v3/5be581aa-89d3-43e3-8478-7186633f8d16"
);
let content = await response.json();
formik.initialValues.email = content[0].email;
} catch (e) {
console.log(e);
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<form onSubmit={formik.handleSubmit}>
<TextField
fullWidth
id="email"
name="email"
label="Email"
value={formik.values.email}
onChange={formik.handleChange}
error={formik.touched.email && Boolean(formik.errors.email)}
helperText={formik.touched.email && formik.errors.email}
/>
<Button color="primary" variant="contained" fullWidth type="submit">
Submit
</Button>
</form >
);
}
Link to SandBox
In addition, I have a second way with using ReactState, but I think it way are lesspreffered but it has another error: After fetching, states fill my input fields, but fail validation. When i'm trying submiting data, to validation sending default values of react State (those that came before fetch)
Link to SandBox with useState way

You were on a good path. Reassigning values to formik instance will not help. Use setFieldValue to set value after fetching data (or simply setValues if you want to set all values):
formik.setFieldValue("email", content[0].email);
or:
formik.setValues(content[0]);
Here's a modified version of your Sandbox:
https://codesandbox.io/s/jolly-sun-uppr6?file=/src/App.js
Don't forget to set your formik to enable reinitialize - https://formik.org/docs/api/formik#enablereinitialize-boolean

Related

Trigger Form Submission from inside React useEffect hook?

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>
)

Custom input component field data not updating after submit in react-hook-form

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?

How to trigger requests with a button using React-query?

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.

Redux forms - submit when no buttons but on field changes

I am creating a redux-form shell with material ui inputs -- I am trying to create a genericForm handler that will allow a field and button object that could be pumped into the component -- I need now to create a form with no submit button due to design - but is able to submit the form on field changes if there are no buttons.
I have a handleChange function that will listen to onChange events for all field types - and bring back the fieldname and new value -- and it now has scope to know if the form hasButtons --- but I am unsure where and how to develop this further to submit the data to the parent if a field is changed
https://redux-form.com/6.0.0-alpha.7/examples/asyncvalidation/
FormShell.js
import React from 'react';
import { reduxForm } from 'redux-form';
import FieldMaker from './FieldMaker';
import ButtonMaker from './ButtonMaker';
const FormShell = props => {
const { handleSubmit, pristine, reset, previousPage, submitting } = props
return (
<form onSubmit={handleSubmit}>
<FieldMaker fields={props.fields} hasButtons={props.buttons.length > 0? true: false} />
<ButtonMaker buttons={props.buttons} pristine={pristine} submitting={submitting} reset={reset} previousPage={previousPage} />
</form>
)
}
export default reduxForm()(FormShell)
GenericForm.js
<FormShell
initialValues={this.props.initialValues}
enableReinitialize={true}//allow form to be reinitialized
fields={this.props.fields}
buttons={this.props.buttons}
form={this.state.uuid}// a unique identifier for this form
validate={this.validateHandler}// <--- validation function given to redux-form
warn={this.warnHandler}//<--- warning function given to redux-form
onSubmit={this.submit}
previousPage={this.props.previousPage}
destroyOnUnmount={this.props.destroyOnUnmount}// <------ preserve form data
forceUnregisterOnUnmount={this.props.forceUnregisterOnUnmount}// <------ unregister fields on unmount
/>
I turned this into a component and added a function that got the field changes from the FieldMaker component
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
import FieldMaker from './FieldMaker';
import ButtonMaker from './ButtonMaker';
class FormShell extends Component {
constructor(props, context) {
super(props, context);
this.fieldChanged = this.fieldChanged.bind(this);
}
fieldChanged(field, value){
console.log("Fields have changed", field, value);
//if it doesn't have any submit buttons -- then submit the form on change of fields
if(!this.props.buttons.length > 0){
console.log("submit the form as a buttonless form");
setTimeout(() => {
this.props.handleSubmit();
}, 1);
}
}
render(){
const { handleSubmit, pristine, reset, previousPage, submitting } = this.props
console.log("THIS FORM SHELL PROPS", this.props);
return (
<form onSubmit={handleSubmit}>
<FieldMaker fields={this.props.fields} fieldChanged={this.fieldChanged} />
<ButtonMaker buttons={this.props.buttons} pristine={pristine} submitting={submitting} reset={reset} previousPage={previousPage} />
</form>
)
}
}
export default reduxForm()(FormShell)

redux-form fields are resetting when changing a value attached to redux state

I've configured a redux-form with three fields as follows:
function mapState(state) {
return {
initialValues: {
terms: state.register.toJS().terms.value
}
};
};
[map dispatch object here]
export const MyFormContainer = reduxForm({
form: 'myForm',
fields: ['email', 'password', 'terms']
}, mapState, mapDispatch)(MyForm);
Each field spreads the passed props as per the docs e.g. {...email}. The terms checkbox contains an onChange which calls an actionCreator to update my redux state:
<input type="checkbox" {...terms} onChange={this.handleTermsToggle.bind(this)}/>
When clicking the checkbox, the email and password fields are both reset to blank, which is not what I want.
Turns out I was approaching the problem from the wrong angle. I needed to remove that chunk of the state tree altogether, and instead, use { change } from 'react-redux' in my outside component to modify the value.
import { change } from 'react-redux'
<button onClick={() => {
this.props.change('myForm', 'terms' true);
}}>Click Me</button>
... and don't forget to map change to mapDispatchToProps

Categories