Formik object doesn't render - reactjs

I created a custom input called <FormInput> and applied useField() and useFormikContext() to it:
const [field, meta] = useField(props);
const { setFieldValue } = useFormikContext();
<FormInput> is part of a library called UI. I'm importing the library and trying to create a very simple form to test, a single field, the only validation being that it's required:
import React, { useContext } from "react";
import { DataContext } from "../../context/DataContext";
import * as UI from "#tui/uilibrary";
import { composeThemeFromProps } from "#css-modules-theme/react";
import styles from "./EnrollStep5.module.scss";
import { Formik, Form } from "formik";
import * as Yup from "yup";
const EnrollStep5 = (props) => {
const context = useContext(DataContext);
const theme = composeThemeFromProps(styles, [context, props]);
return (
<Formik
initialValues={{
name: "",
}}
validationSchema={Yup.object().shape({
name: Yup.string().required("Required"),
})}
>
{(props) => {
<Form className={theme.EnrollStep2}>
<UI.FormInput type={"text"} name={"name"} label={"Name"} />
</Form>;
}}
</Formik>
);
};
export default EnrollStep5;
This comes up blank. The Formik object appears in the Component browser, but shows as if it has no children. I have the feeling this is just due to inexperience and that I'm close. What am I doing wrong?

In short:
Wrap the <Form>...</Form> in (parantheses) instead of {curly braces}.
Little more detailed:
So basically what is happening in your code with the curly braces is you are entering the callback function. So the <Form>...</Form> is just floating around somewhere in the function body. What you intend to do is to return it.
So you can either add a return statement (return <Form>...</Form>;) inside the curly braces or directly return the value without entering the function body. E.g. like (props) => <Form>...</Form> or (props) => (<Form>...</Form>), whichever you prefer.

Related

How can I use a toast of Chakra UI throughout all the components?

I am trying to show an alert when user makes any request in my react application. The thing is that currently I am using separate toast components for separate components. How should I use one single toast component throughout the whole application. I tried putting the toast component in App.jsx but in order to manage the toast message and color I have to do prop-drilling, which I want to avoid. I am using redux so I can not use useContext for managing the toast. Any idea would be appreciated.
I prefer using a higher-order component called HOC to wrap the toast and provide the necessary props to it. This way, you can keep the toast in a central location and use the HOC to wrap other components that need to display the toast.
For example:
// withToast.js
import { useState } from "react";
import { ToastProvider } from "#chakra-ui/core";
const withToast = (WrappedComponent) => {
return (props) => {
const [toast, setToast] = useState({
message: "",
color: "",
isOpen: false,
});
const showToast = (message, color) => {
setToast({ message, color, isOpen: true });
};
const hideToast = () => {
setToast({ message: "", color: "", isOpen: false });
};
return (
<ToastProvider>
<WrappedComponent
{...props}
showToast={showToast}
hideToast={hideToast}
toast={toast}
/>
</ToastProvider>
);
};
};
export default withToast;
Now you can use the same toast in every component that is being wrapped by withToast:
import React from 'react';
import withToast from './withToast';
const App = (props) => {
const { showToast, toast } = props;
return (
<div>
<button onClick={() => showToast("Hello, World!", "green")}>
Show Toast
</button>
<Toast message={toast.message} color={toast.color} isOpen={toast.isOpen} />
</div>
);
};
export default withToast(App);
You can also wrap multiple components in the HOC and use the showToast and hideToast functions in any component that is rendered within the wrapped component, this way you don't have to prop-drill showToastand hideToast.

react hook form not getting context on nested component

I am having multiple tab, each tabs load different components. In each components I have different set of forms. So there is a generic footer where user can click on save or continue.
So I am trying to use the react hook form useContext, but I am not getting the values.
I have reproduced the same issue on the below code.
SaveAndContinue component
import React from "react";
import { useFormContext } from "react-hook-form";
const Footer = (props) => {
const { formState, handleSubmit } = useFormContext();
const onSaveDetails = (data) => {
console.log("onSaveDetails", data);
};
const onContinue = (data) => {
console.log("onContinue", data);
};
return (
<>
<button
disabled={!formState?.isDirty}
onClick={handleSubmit(onSaveDetails)}
>
Save
</button>
<button disabled={!formState?.isDirty} onClick={handleSubmit(onContinue)}>
Continue
</button>
</>
);
};
export default Footer;
How can I get the formData of each component form when clicking on the save or continue button using react hook form context
Any help is appreciated
Here is the codesandbox
I looked at your codesandbox. the problem is:
on your ServiceDetails and UserDetails components you should register your inputs using useFormContext not useForm:
const { register } = useFormContext({
mode: "onBlur",
reValidateMode: "onChange",
defaultValues: {
firstName: ""
}
});

How do I populate Select Options from Redux store?

I am using react-select
I have a form that creates a new contact. In my redux store I have groups that are already created. When I click the Select to show the options I would like to load the options from my redux store.
The "groups" from redux store has only one value, that is title: String in the GroupModel in the backend.
I understand that react-select needs to have a label: '', value: ''
If I create an array myself and pass the values in it works fine. But with redux nothing is working. I cant find any answers anywhere online which seems trivial to me....
Here is my component below
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import Select from 'react-select';
import { createContact } from '../../actions/index';
import { Button, Form, FormGroup, Input, Label } from 'reactstrap';
import Divider from '../common/Divider';
const ContactForm = ({ hasLabel }) => {
const dispatch = useDispatch()
// State
const [contact, setContact] = useState({
group: '',
})
// Submit Handler
const handleSubmit = e => {
e.preventDefault()
dispatch(createContact(contact))
};
// Change Handler
const handleChange = e => {
setContact({...contact, [e.target.name]: e.target.value})
};
// bringing in our state from redux
const groups = useSelector((state) => state.groups)
return (
<>
<Form onSubmit={handleSubmit}>
<div>
<FormGroup>
<Label>
Choose Group/List</Label>
<Select
name="group"
options={groups}
getOptionlabel={({title}) => title}
getOptionValue={({_id}) => _id }
onChange={() => {}}
isMulti
/>
</FormGroup>
</div>
</Form>
</>
);
};
ContactForm.propTypes = {
hasLabel: PropTypes.bool
};
ContactForm.defaultProps = {
layout: 'basic',
hasLabel: false
};
export default ContactForm;
Ok.... So my solution above was pretty much correct. The issue I had was the getOptionlabel needed to be getOptionLabel <--- notice I forgot to capitalize the L in label....
I hope someone who needs to use react-select with redux finds this post and it helps.
So basically just bring in your redux state with useSelector or connect,
then make sure to use the props below in your Select component
getOptionLabel={({title}) => title}
getOptionValue={({_id}) => _id}

Unable to use react form data in useEffect hook

I have an antd form where I am able to get the form data in onFinish function upon hitting submit button in which I wanted to use uesEffect hook and dispatch an action with form data as payload to redux saga but I got following error
React Hook "useEffect" is called in function "onFinish" that is neither a React function component nor a custom React Hook function.
If I write useEffect hook outside the onFinsh function, I am unable to get the form data/values
Please suggest a workaround to get the form data values outside of onFinish function
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Form, Input, Button, Checkbox } from 'antd';
const Demo = () => {
const onFinish = (values) => {
// alert(JSON.stringify(values['username']));
useEffect(() => {
// dispatch an action with values as payload
}, []);
};
console.log(values) // UNABLE TO GET VALUES HERE...HOW TO GET IT???
return (
<Form
name="basic"
onFinish={onFinish}>
<Form.Item
label="Username"
name="username">
<Input />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
);
};
ReactDOM.render(<Demo />, document.getElementById('container'));
It looks like you don't even need the useEffect() hook. Just dispatch the action from within the onFinish() and have state store the values
const Demo = () => {
const [ values, setValues ] = useState([]);
const onFinish = (recievedValues) => {
// dispatch here
setValues(recievedValues);
}
console.log(values) // <-- you can get it here
return (<div> ... </div>);
};
Or better yet, since you are already saving the values in redux during dispatch, you should use that in your render code as well:
import { useSelector } from 'react-redux';
const Demo = () => {
//point to the state where your data is
const stateValues = useSelector(state => state.your.data);
const onFinish = (recievedValues) => {
// dispatch here
}
console.log(stateValues) // <-- you can get it here
return (<div> ... </div>);
};
useEffect can only be called at the top level of your component, not within a function. In this case, you shouldn't need useEffect to dispatch the action, and instead can just do so directly inside onFinish.

Higher Order Components, Props with Formik & GatsbyJS

I am trying to create a Formik form in a Gatsby site using the withFormik higher order component (rather than use a render prop).
Here is a simplified version of my code:
import React from 'react'
import { withFormik } from 'formik'
const TestPage = ({ handleChange, values }) => (
<div>
<input
type="email"
name="email"
placeholder="Email"
onChange={handleChange}
value={values.email}
/>
</div>
)
const FormikTest = withFormik({
mapPropsToValues() {
return {
email: 'test#test.com',
}
},
})
export default FormikTest(TestPage)
So far, everything works just as a I want. However, I am hitting a problem when it comes to setting up a conditional argument for the email field in the mapPropsToValues object. You can see what I am trying to do by watching about 1 minute of this tutorial (it's set to the right starting time): https://www.youtube.com/watch?v=yNiJkjEwmpw&feature=youtu.be&t=717
The problem is that I can't figure out how I would send props to the mapPropsToValues using Gatsby. I don't have access to render like in that tutorial.
In other words, in Create React App, you can do something like the following:
const FormikTest = withFormik({
mapPropsToValues({ email }) {
return {
email: email || '',
}
},
})(TestPage)
render(<FormikTest email="test#test.com />, document.getElementById('#root'))
But I don't have access to render in Gatsby or a <FormikTest /> component.
Any idea, therefore, how I could pass in props to mapPropsToValues so that I could conditionally set initial values for the email form using Gatsby?
Thanks.
UPDATE
I have created a simple Codesandbox version which has just one page using Formik. Here is the link: https://codesandbox.io/s/gatsby-starter-default-270gs?fontsize=14
And here is the code for that page:
import React from "react"
import { withFormik } from "formik"
const IndexPage = ({ handleChange, values }) => (
<div>
<input
type="email"
name="email"
placeholder="Email"
onChange={handleChange}
value={values.email}
/>
</div>
)
const FormikTest = withFormik({
mapPropsToValues() {
return {
email: "",
}
},
})
export default FormikTest(IndexPage)
In that tutorial, he's defining everything in one file hence the reason he's using render (it's what react does under the hood when you import components). In your case, the TestPage component would be used as a child in another component like:
render(){
return (<TestPage email = "test#example.com"/>);
}
mapPropsToValues would get the props passed in as a parameter and "email" would be a member of those props
You have access to any props passed down to a component in mapPropsToValues. Try passing your base component through withFormik straight away while returning a function as an export - that will allow you to set any desired props.
const FormikTest = withFormik({
mapPropsToValues(props) {
console.log(props) // { rad: "yas" }
return {
email: "",
}
},
})(IndexPage)
export default () => <FormikTest rad="yas" />

Resources