Hi, thanks for great plugin!
I have the component AddProduct, wich works, and adds product to redux store. It looks like this (without formik):
import React from 'react';
import { connect } from 'react-redux';
import { addProduct } from '../actions';
const AddProduct0 = ({ dispatch }) => {
let inputSKUNumber;
let inputProductName;
return (
<div>
<input
ref={(node) => {
inputSKUNumber = node;
}}
placeholder="SKU Number"
/>
<input
ref={(node) => {
inputProductName = node;
}}
placeholder="Product name"
/>
<button
onClick={() => {
dispatch(addProduct({ SKUNumber: inputSKUNumber.value, name: inputProductName.value }));
inputSKUNumber.value = '';
inputProductName.value = '';
}}
>
Add Product
</button>
</div>
);
};
const AddProduct = connect()(AddProduct0);
export default AddProduct;
Today I tried to rewtrite this component using Formik (in full version there is 7 fields with validation, but for more clear example I explain here only 2 fields, like in old version). Validation works, and I see "variables" and other props, when I change fields. I liked Formik. That how it looks my form with it:

But I can't dispatch the action to the redux store. Can somebody answer, how to use connect in right way with HOC (container component) EnhancedForm, and presentational component MyInnerForm. It's how looks my new code with Formik:
import React from 'react';
import { connect } from 'react-redux';
import { withFormik } from 'formik';
import Yup from 'yup';
import { addProduct } from '../actions';
import './helper.css';
// Our inner form component. Will be wrapped with Formik({..})
const MyInnerForm = (props) => {
const {
values,
touched,
errors,
dirty,
isSubmitting,
handleChange,
handleBlur,
handleSubmit,
handleReset,
} = props;
return (
<form onSubmit={handleSubmit}>
<label htmlFor="SKUNumber">SKU Number</label>
<input
id="SKUNumber"
placeholder="SKU Number"
type="number"
value={values.SKUNumber}
onChange={handleChange}
onBlur={handleBlur}
className={errors.SKUNumber && touched.SKUNumber ? 'text-input error' : 'text-input'}
/>
<div className="input-feedback">{touched.SKUNumber ? errors.SKUNumber : ''}</div>
<label htmlFor="productName">Product Name</label>
<input
id="productName"
placeholder="Product Name"
type="text"
value={values.productName}
onChange={handleChange}
onBlur={handleBlur}
className={errors.productName && touched.productName ? 'text-input error' : 'text-input'}
/>
<div className="input-feedback">{touched.productName ? errors.productName : ''}</div>
<button
type="button"
className="outline"
onClick={handleReset}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
};
const EnhancedForm = withFormik({
mapPropsToValues: () => ({
SKUNumber: 12345678,
productName: 'Default Product',
}),
validationSchema: Yup.object().shape({
SKUNumber: Yup.number()
.max(99999999, 'SKU Number must be less than 8 digits')
.required('SKU Number is required!'),
productName: Yup.string()
.min(5, 'Product name must be longer than 5 symbols')
.max(50, 'Product name must be shorter than 50 symbols')
.required('Product name is required!'),
handleSubmit: (values, { setSubmitting }) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
setSubmitting(false);
}, 1000);
// dispatch(addProduct(values));
},
displayName: 'BasicForm', // helps with React DevTools
})(MyInnerForm);
export const DisplayFormikState = props => (
<div style={{ margin: '1rem 0' }}>
<h3 style={{ fontFamily: 'monospace' }} />
<pre
style={{
background: '#f6f8fa',
fontSize: '.65rem',
padding: '.5rem',
}}
>
<strong>props</strong> = {JSON.stringify(props, null, 2)}
</pre>
</div>
);
const AddProduct = connect()(EnhancedForm);
export default AddProduct;
I used this "Basic example" to make my first step in formik.
Also I have written the same question on Stackowerflow.
Dispatch must be already accessible form your props. And props are passed to submit function.
Simple example:
const handleSubmit = (values, { props }) => props.dispatch(someAction(values));
@connect(...)
@withFormik(
handleSubmit,
...,
)
class FormPresentation extends Component {
render() {
const { handleSubmit, ... } = this.props;
return() {
<form onSubmit={handleSubmit}>
/* fields */
</form>
}
}
}
Now you can use dispatch from props, the same can be accomplished without class, this example with decorators is just more readable
You can see here what you will get in handleSubmit as second argument.
https://github.com/jaredpalmer/formik#handlesubmit-values-values-formikbag-formikbag--void
I also discourage using decorators, because it's a still highly experimental feature and if I am not mistaken, current Babel implementation is not even spec complaint. Just use HOCs.
@sinkalex31 thanks, your advice helped me. it works:
handleSubmit(values, { props, setSubmitting }) {
props.dispatch(addProduct(values));
setSubmitting(false);
// resetForm();
},
do you know how can I reset form after submition to default values, witch are calculating in mapPropsToValues?
@prichodko what kind of HOC? HOC above EnhancedForm?
I get Connect example in Dan Abramov's Redux lessons
@Aparus I'm trying to do the same but I can't access or maybe attach dispatch and my redux action at formik. How did you make it work?
@ianemv I didn't remember ) I shared code with you on bitbacket. Try to get answer from repo )
Thanks. I already got it working and will check out your shared code too.
@Aparus or @ianemv I am having the same issue. Not sure why I can't get attach dispatch & the action. Do you have a working example you can share?
@alitinker I forgot what sample from formik I replicated but what I did was connect (redux-react) my formik form. Since I am using stateful component, I then applied formik to my component.
const FormikPost= withFormik({
});
connect(mapStateToProps,{postFunction})(FormikPost)
class MyComponent extends React.Component{
}
const HOCForm = FormikPost(MyComponent);
export default connect(mapStateToProps,null)(HOCForm );
Maybe there are more better ways than what I did and share it also here.
@ianemv Thank you. I had to set mine up a little differently because I'm using bindActionCreators and used the input primitives approach but the concept is mostly the same. Seems to be working now. Here for anyone who finds this useful:
import React from 'react';
import { withFormik } from 'formik';
import * as Yup from 'yup';
import * as Fields from '../../shared/layout/components/forms-component';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { actionCreators } from '../../shared/auth/store/auth-store';
class LoginForm extends React.Component {
render() {
const {
values,
touched,
errors,
dirty,
handleChange,
handleBlur,
handleSubmit,
handleReset,
isSubmitting,
} = this.props;
return (
<div className="forms-styles">
<form onSubmit={handleSubmit}>
<div>
<div className="single-row">
<Fields.TextInput
id="userId"
type="text"
label="User ID"
placeholder="User ID"
error={touched.userId && errors.userId}
value={values.userId}
onChange={handleChange}
onBlur={handleBlur}
/>
</div>
<div className="single-row">
<Fields.TextInput
id="userPassword"
type="password"
label="Password"
placeholder="Password"
error={touched.userPassword && errors.userPassword}
value={values.userPassword}
onChange={handleChange}
onBlur={handleBlur}
/>
</div>
<div className="single-row">
<button className="button secondary" type="submit" disabled={isSubmitting}>
Login
</button>
</div>
</div>
</form>
</div>
);
}
};
const formikEnhancer = withFormik({
validationSchema: Yup.object().shape({
userId: Yup.string()
.required('User ID is required.'),
userPassword: Yup.string()
.required('Password is required.')
}),
mapPropsToValues: () => ({
userId: 'test',
userPassword: 'test'
}),
handleSubmit: (payload, { props, setSubmitting }) => {
props.loginUser(payload);
setSubmitting(false);
},
displayName: 'LoginForm',
})(LoginForm);
const Login = connect(
state => state.login,
dispatch => bindActionCreators(actionCreators, dispatch)
)(formikEnhancer)
export default Login;
@sinkalex31 It's worth to mention, that if you pass mapDispatchToProps to the connect then the method dispatch won't be available in the handleSubmit as props.
Example 1:
connect(mapState, mapDispatch)(withFormik({
handleSubmit: (values, {props}) => console.log(props.dispatch) // undefined
})(Form))
Example 2:
connect(mapState, null)(withFormik({
handleSubmit: (values, {props}) => console.log(props.dispatch) // function
})(Form))
this is how i did it using mapDispatchToProps
mapPropsToValues: props => ({
...props
}), then in the handleSubmit
handleSubmit: (values, bag) => {
setTimeout(() => {
if (values.firstName === 'admin') {
bag.setErrors({ firstName: 'Nice try!' });
} else if (values.lastName === 'admin') {
bag.setErrors({ lastName: 'Nice try!' });
} else {
console.log('values ', values)
values.signUp(values)
bag.resetForm()
}
bag.setSubmitting(false);
}, 2000)
const mapDispatchToProps= (dispatch) => {
// console.log('values', values)
return{
signUp: (values) => dispatch(signUp(values))
}
}
if there's a better way please let me know
How about this for a HOC:
Note that "someAction" is forwarded to the withFormik and accessed through "props" in the "handleSubmit" Api
import { connect } from 'react-redux';
import { withFormik } from 'formik';
import SomeComponent from './some.component.jsx';
const Form = withFormik({
...
handleSubmit: (values, { props }) => {
props.someAction(values); // <<--- Some action passed down from redux connect
},
})(SomeComponent);
const mapStateToProps = state => ({
...
});
const mapDispatchToProps = dispatch => ({
someAction: values => // <<-- Here's where the action is passed down!!
dispatch({
type: 'SomeAction',
payload: values,
}),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(Form);
`
@alitinker Thank you! That was the exact snippet I needed to get my code working, and understand how formik + redux fit together.
Hi I was just wondering if someone had come up with a way to use props from redux in the validation schema? I'm connecting to a redux store for translations and using those in my validation messages and am having a bit of trouble wiring it up.
I'm trying to work out if withFormik specifies the validationSchema I don't know how to achieve the following for example
validationSchema: `object().shape({
dateOfHospitalAdmission: mixed().required().test('valid-date', `${translate(this.props, 'ValidationMessageDate')} ${translate(this.props, 'DateFormat')}`, (val: string) => {
return moment(val, 'DD/MM/YYYY').isValid();
}),
Where this.props contains translations from a redux store
Most helpful comment
@ianemv Thank you. I had to set mine up a little differently because I'm using bindActionCreators and used the input primitives approach but the concept is mostly the same. Seems to be working now. Here for anyone who finds this useful: