I'm trying to do this with yup and react-hook-forms
For example, if I'm given an array of ids, then I would like to have a field for each id. The ids are random (i.e., we could have 4 ids or 100 ids). For now, I just want to see if all the input is filled (.required())
This is how I would handle validation without any libraries
export default function App(){
const [ids, setIds] = React.useState(arr1)
const inputValues = React.useRef({});
const handleSubmit = () => {
const { current: values } = inputValues;
console.log(values);
};
const validateInput = event => {
const { name, value } = event.target;
// validation done here
if(true){
inputValues.current[name] = value;
}
};
return (
<div>
<form onSubmit={handleSubmit}>
{ids.map(num => (
<input name={num} onChange={validateInput} required key={num} />
))};
<button type="submit">submit</button>
</form>
</div>
);
}
https://stackblitz.com/edit/react-ts-4jnfx2?file=App.tsx
Now how would I do this with yup and react hook forms to validate the input ?
As per the sandbox below, you can find an implementation with React Hook Form as shown here;
As it's pretty easy to follow from it's own documentation here, you don't need any other variables. So that I made it a bit simplification and only used necessary handleSubmit function, instead of validateInput and references.
import { useState } from "react";
import { useForm } from "react-hook-form";
export function App() {
const { register, handleSubmit } = useForm();
const [data, setData] = useState("");
const [ids] = useState(["1", "3", "40", "18"]);
return (
<form
onSubmit={handleSubmit((data) => {
console.log(data);
setData(JSON.stringify(data));
})}
>
{ids.map((num) => (
<input key={num} name={num} {...register(num, { required: true })} />
))}
<p>{data}</p>
<input type="submit" value="submit" />
</form>
);
}
Please check below code. I don't have experience with TypeScript so I have wrote it in JavaScript
App.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import * as yup from 'yup';
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
const createSchema = (keys, valueSchema) => yup.object().shape(keys.reduce((acc, key) => ({ ...acc, [key]: valueSchema }), {}));
const mySchema = createSchema(arr1, yup.string().required());
const App = () => {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(mySchema),
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
{arr1.map((item) => (
<div key={item} style={{ border: '1px solid black' }}>
<input {...register(`${item}`)} />
<p>{errors?.[item]?.message}</p>
</div>
))}
<input type="submit" />
</form>
);
};
export default App;
Related
I have a form where i need to save the values as an array:
function App() {
const {
setValue,
register,
handleSubmit,
watch,
formState: { errors }
} = useForm();
const onSubmit = (data) => {
console.log(data, "submit");
}; // your form submit function which will invoke after successful validation
const allValues = watch("example") || [];
console.log(allValues);
return (
<form onSubmit={handleSubmit(onSubmit)}>
{[1, 2, 3].map((v, index) => {
return (
<input value={v} type="checkbox" {...register(`example[${index}]`)} />
);
})}
<input
onChange={(e) => {
setValue("example", e.target.value);
}}
/>
<input type="submit" />
</form>
);
}
Here: setValue("example", e.target.value); i want to set my text input value inside the result, so when i add check some checkboxes like 1 and 2 and also add test value in text input, when i will save i need to get the next result [1,2,'test']. If there is not a value in text input i should't add anything in the array. question: How to solve the issue? Now if i add test in array i get [t,e,s,t]. demo: https://codesandbox.io/s/react-hook-form-get-started-forked-q2xhj5?file=/src/index.js:129-840
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
function App() {
const { setValue, getValues, register, handleSubmit } = useForm();
const [inputValue, setInputValue] = useState("");
const onSubmit = (data) => {
if (inputValue) setValue("example", [...getValues().example, inputValue]);
console.log(getValues().example);
}; // your form submit function which will invoke after successful validation
return (
<form onSubmit={handleSubmit(onSubmit)}>
{[1, 2, 3].map((v, index) => {
return (
<input value={v} type="checkbox" {...register(`example[${index}]`)} />
);
})}
<input
value={inputValue}
onChange={(e) => {
setInputValue(e.target.value);
}}
/>
<input type="submit" />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
This is my solution. Some changes I made:
I only add the input value to the "example" array onSubmit, not onChange.
I made the input controlled, giving it a value property inputValue, and setting it in the onChange handler
I made use of getValues function, to obtain the current elements of example array. Then I used setValue with the spread operator to add at the end of the array.
Feel free to ask me further questions!
--Ado
Edit 1: turns out I got it wrong, you don't want to add the input value to example every time on submit. Here is my new solution:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import { useForm } from "react-hook-form";
import "./styles.css";
function App() {
const { getValues, register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(getValues().example);
}; // your form submit function which will invoke after successful validation
const checkboxes = [1, 2, 3];
return (
<form onSubmit={handleSubmit(onSubmit)}>
{checkboxes.map((v, index) => {
return (
<input value={v} type="checkbox" {...register(`example[${index}]`)} />
);
})}
<input
{...register(`example[${checkboxes.length}]`)}
/>
<input type="submit" />
</form>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
So I have been trying to make a form for adding a student in my website and everything is registering correctly except for my picture.
import { useEffect } from "react";
import { SubmitHandler, useForm } from "react-hook-form";
import FormInputField from "../../Components/FormInputField/FormInputField";
import { AddStudent } from "../../Types";
import { AddStudentFormFields } from "../../strings";
import style from "./AddStudentForm.module.css";
import { doc, getFirestore, setDoc } from "firebase/firestore";
const AddAStudent = () => {
const { register, handleSubmit, formState, reset } = useForm<AddStudent>();
const onsubmit: SubmitHandler<AddStudent> = (data) => {
console.log(data.newphotp.name);
return;
const db = getFirestore();
const colRef = doc(db, "Students", data.UniversityID);
setDoc(colRef, data);
};
useEffect(() => {
if (formState.isSubmitSuccessful) reset();
}, [formState, reset]);
return (
<div>
<form className={style.form} onSubmit={handleSubmit(onsubmit)}>
<div>
{AddStudentFormFields.map((item, index) => (
<FormInputField
key={index}
register={register}
name={item.field}
label={item.label}
/>
))}
<input type="file" {...register("newphotp")} />
</div>
<button className={style.button}>Add student </button>
<button type="reset" className={style.button}>
Reset form
</button>
</form>
</div>
);
};
export default AddAStudent;
I have tried to figure it out since an hour but it is not working sadly.
version of react-hook-form is 7.40.0
The input of type file return an array of type file so this :
console.log(data.newphotp[0].name)
worked fine.
I have a Formik form which uses check-boxes and selects. For the select field I am using react-select. There is no problem to submit the form but when I want to reset the form the react-select part is not clearing and also when I want to prefill the form after a push of a button with setFieldValue, again the react-select part is not responding.
How can I make it work this form with react-select within Formik?
https://codesandbox.io/s/wonderful-breeze-dyhi3b
App.js
import React from "react";
import FormikReactSelect from "./components/FormikReactSelect";
function App() {
return <FormikReactSelect />
}
export default App;
FormikReactSelect.js
import React, {useRef} from "react";
import {Form, Field, Formik, FieldArray} from "formik";
import SelectField from "./SelectField";
function FormikReactSelect() {
const formikRef = useRef();
const drinkDist = ["Wine", "Beer", "Whiskey"];
const countryDist = ["US", "FR", "DE", "BE", "IT"];
const arrToValLab = (arr) => {
if (arr !== undefined) {
const valLab = arr.map((t) => ({
value: t,
label: t,
}));
return valLab;
} else {
return null;
}
};
const onClick = () => {
if (formikRef.current) {
formikRef.current.setFieldValue("drinks", ["Whiskey"]);
}
if (formikRef.current) {
formikRef.current.setFieldValue("countries", ["FR"]);
}
};
return (
<>
<Formik
innerRef={formikRef}
enableReinitialize={true}
initialValues={{drinks: [], countries: []}}
>
{({
values,
handleChange,
handleSubmit,
handleBlur,
resetForm,
setFieldValue,
}) => (
<Form noValidate>
Drinks:
<FieldArray
name="drinks"
render={(arrayHelpers) => (
<div>
{drinkDist.map((r, i) => (
<div key={i}>
<label>
<Field
name="drinks"
type="checkbox"
value={r}
checked={values.drinks.includes(r)}
onChange={(e) => {
if (e.target.checked) {
arrayHelpers.push(r);
} else {
const idx = values.drinks.indexOf(r);
arrayHelpers.remove(idx);
}
}}
/>
{" " + r}
</label>
</div>
))}
</div>
)}
/>
Countries:
<Field
component={SelectField}
name="countries"
options={arrToValLab(countryDist)}
/>
<button
type="button"
onClick={() => {
resetForm();
}}
>
Reset
</button>
{JSON.stringify(values, null, 2)}
</Form>
)}
</Formik>
Sending preset values:
<button onClick={onClick}>Set Field</button>
</>
);
}
export default FormikReactSelect;
SelectFireld.js
import React from "react";
import Select from "react-select";
import {useField} from "formik";
export default function SelectField(props) {
const [field, state, {setValue, setTouched}] = useField(props.field.name); // eslint-disable-line
const onChange = (value) => {
let arrValue = [];
value.map((k) => arrValue.push(k.value));
setValue(arrValue);
};
return <Select {...props} isMulti onChange={onChange} onBlur={setTouched} />;
}
I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
Hi I am new to React and I just can't wrap my head around some basic things.
One of these things is the following.
Why does the state of itemData change when calling the function with onChange on the textarea element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form>
<textarea
onChange={handleitemData}
placeholder="What you have in mind ..."
></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
But not when calling the function with onSubmit on the form element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
I know the React way is if that does not work try something else but I am trying to actually understand what is going on.
I apreciate your response.
You must prevent the default behavior of the browser on submit:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
//The line below is necessary
e.preventDefault()
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
attached is a sandbox to see it in action https://codesandbox.io/s/infallible-lederberg-3oj1w?file=/src/App.js:0-630