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)
Related
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>
)
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 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
I am building form components - and I want to be able to give my own unique identifiers in the exports param. I also want to learn how to push validation schemas into these export sections. I need to get more control in this section otherwise all forms are thinking they are the "'syncValidationForm'"
import React from 'react';
import { reduxForm } from 'redux-form';
import validate from './validateForm';
import warn from './warnForm';
import FieldMaker from './FieldMaker';
import ButtonMaker from './ButtonMaker';
const FormShell = props => {
const { handleSubmit, pristine, reset, submitting } = props
return (
<form onSubmit={handleSubmit}>
<FieldMaker fields={props.fields} />
<ButtonMaker buttons={props.buttons} pristine={pristine} submitting={submitting} reset={reset} />
</form>
)
}
export default reduxForm({
form: 'syncValidationForm', // a unique identifier for this form
validate, // <--- validation function given to redux-form
warn // <--- warning function given to redux-form
})(FormShell)
You can create a factory function and export it:
function makeForm(id) {
return reduxForm({
form: id,
validate,
warn
})(FormShell)
}
export default makeForm;
And then you can create a form with custom id:
import makeForm from './FormShell';
const FormShell = makeForm('syncValidationForm');
// ...
This is a very specific issue I have searched this site and the web for hours trying to resolve it. Mod please don't disable this post with out reading completely.
I can pass function to control the state easily.
I can pass objects arrays to modal easily
This issue is specifically passing a function to a modal which contains a registration form - upon completion of the form I want to change the state.
class Users extends Component {
aPropVal = 'works fine';
// passing the following function as prop to any other (non-modal) component works fine
addUserStart = (data) => {
console.log('addUserStart has been fired')
}
render() {
return (
<div id="main">
<ModalAddUser addUserStart={this.addUserStart} aprop={this.aPropVal} />
...
</div>
)
}
}
export default Users
then the ModalAddUserObject which works perfectly in every way - exception being the function won't pass
import React, { useState } from 'react';
import { Button, FormGroup, Input, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
const ModalAddUser = (props) => {
console.log('props for ModalAddUser: ' + JSON.stringify(props))
...
}
console.log =
props for ModalAddUser: {"aprop":"works fine"}
JSON.stringify wouldn't serialize functions. if you try console.log(props) you should see your functions.