Skip to content

Instantly share code, notes, and snippets.

@Aniganesh
Last active March 8, 2022 17:29
Show Gist options
  • Save Aniganesh/68085288cf957290df93a564df962840 to your computer and use it in GitHub Desktop.
Save Aniganesh/68085288cf957290df93a564df962840 to your computer and use it in GitHub Desktop.
How to use formik with multiple fields in the same value
import { Form, Formik } from "formik";
import React, { useState } from "react";
import "./App.css";
/* To view the result in a browser, create a new react app and replace the existing App.jsx file with this file */
function App() {
const [numAddresses, setNumAddresses] = useState(1);
const localStorageAddresses = JSON.parse(
localStorage.getItem("savedAddresses") || "[]"
);
return (
<FormContextProvider>
<FormContext.Consumer>
{({ savedAddresses, setSavedAddresses }) => (
<div className="App">
<Formik
initialValues={{
addresses: savedAddresses || localStorageAddresses || [],
// values in the form when loaded for the first time or whenever this value is changed
}}
enableReinitialize={true} // important parameter. will allow setting the new values of the form using initialValues
onSubmit={({ addresses }, helpers) => {
console.log({ addresses });
// On submitting form, save in context and local storage. It is not necessary to store in both. Any one is enough. Saving in both just to show an example
setSavedAddresses(addresses);
localStorage.setItem(
"savedAddresses",
JSON.stringify(addresses)
);
helpers.resetForm();
}}
>
{({ handleChange }) => {
console.log({ savedAddresses });
return (
<Form>
{/* Create an array of length 'numAddresses' */}
{Array.from({ length: numAddresses }).map((_, index) => (
/* All entries in the array will have undefined as value by default. So ignoring the first parameter when mapping through the array */
<div
key={index}
style={{
padding: 8,
width: "fit-content",
margin: "0 auto",
}}
>
<div
style={{
paddingTop: 8,
paddingBottom: 8,
textAlign: "left",
}}
>
Address {index + 1}
</div>
<label htmlFor={`addresses[${index}].line1`}>
Address line 1
</label>
<input
name={`addresses[${index}].line1`}
onChange={handleChange}
/>
<label htmlFor={`addresses[${index}].line2`}>
Address line 2
</label>
<input
name={`addresses[${index}].line2`}
onChange={handleChange}
/>
<label htmlFor={`addresses[${index}].line3`}>
Address line 3
</label>
<input
name={`addresses[${index}].line3`}
onChange={handleChange}
/>
{/* MUI equivalent:
<TextField name={`addresses[${index}].line3`} onChange={handleChange} label="Address line 3" />
*/}
</div>
))}
<div>
<button
type="button"
onClick={() => {
setNumAddresses(numAddresses + 1);
/* Increase number of addresses */
}}
>
Add address
</button>
<button type="submit">Submit</button>
</div>
</Form>
);
}}
</Formik>
</div>
)}
</FormContext.Consumer>
</FormContextProvider>
);
}
export default App;
const FormContext = React.createContext({
savedAddresses: [],
setSavedAddresses: (addresses /* Array of addresses */) => {},
});
const FormContextProvider = ({ children }) => {
const [savedAddresses, setSavedAddresses] = useState([]);
console.log({ savedAddresses }, " in FormContextProvider");
return (
<FormContext.Provider value={{ savedAddresses, setSavedAddresses }}>
{children}
</FormContext.Provider>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment