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);
Related
In this simple "demo" in CodeSandbox: https://codesandbox.io/s/cool-fast-fi426k?file=/src/App.tsx
you can see that checked actually changes, based on an external condition (inputted text length), but this does not "physically"change the Switch
import "./styles.css";
import * as React from "react";
import { Switch } from "antd";
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm();
let [checked, setChecked] = React.useState(false);
const onSubmit = (data: string) => {
console.log("data.text: ", data.text);
let length = data.text.length;
console.log("data.text.length: ", length);
if (length > 5) {
console.log("'checked' variable has to be set as TRUE");
setChecked((checked) => true);
} else {
console.log("'checked' variable has to be set as FALSE");
setChecked((checked) => false);
}
};
const AntdOnChange = (checked) => {
console.log(`switch to ${checked}`);
};
React.useEffect(() => {
AntdOnChange(checked);
}, [checked]);
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label htmlFor="text">Text</label>
<input id="text" placeholder="text" {...register("text")} />
</div>
<button type="submit">Submit</button>
</form>
<Switch
checkedChildren="ON"
unCheckedChildren="OFF"
defaultChecked
onChange={AntdOnChange}
/>
</div>
);
}
How to pass the correctly changed value of checked variable to the Switch state ?
You can achieve this by doing three things
Remove the useEffect that updates the checked state. If you pass the state into the switch component with that, it will cause multiple renders which will cause an error
Do this in your switch component. I have added the checked prop
<Switch
checkedChildren="ON"
unCheckedChildren="OFF"
defaultChecked
checked={checked}
onChange={AntdOnChange}
/>
In your AntdOnChange function, do this. This function will work independently of whatever is added in the input
const AntdOnChange = (checked) => {
setChecked((checked) => !checked);
};
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;
I'm new to React and still am trying to make sense of a few things. I'm using the react-form-dom library and after looking into the documentation I can't figure out what's going on with my project.
I have created a custom input component that takes in a few parameters. The problem is that when I try to log the value of the inputs I get undefined.
Input.jsx
import React, { useState } from 'react';
import './Input.scss';
export default function Input(props, {register, errors}) {
const [inputVal, setInputVal] = useState('');
const nameChange = (e) => {
setInputVal(e.target.value);
props.nameChange(e.target.value);
}
const legend = <span className='legend'>{props?.legend}</span>;
const showLength = <span className='legend'>{props.maxLength - inputVal.length}</span>;
return (
<div className="input-holder">
<input
ref={register}
type={props.type || 'text'}
id={props.id}
placeholder={props.placeholder}
value={inputVal}
onInput={nameChange}
maxLength={props.maxLength || ""}
/>
{ props.legend ? legend : null}
{ props.showLength && props.maxLength ? showLength : null }
</div>
)
}
Form.jsx
import React from 'react';
import { useForm } from "react-hook-form";
import Button from './Button';
import Input from './Input';
export default function UserForm(props) {
const { register, handleSubmit } = useForm();
function submitForm(data) {
console.log(data)
}
return (
<div className="user-form">
<form onSubmit={handleSubmit(submitForm)}>
<Input
placeholder="Name"
nameChange={(name) => props.userName(name)}
showLength="true"
maxLength="35"
legend="Remaining Char: "
id="name"
register={register}
/>
<Input
placeholder="E-mail"
type="email"
id="email"
register={register}
/>
<Button label="Buy" />
</form>
</div>
)
}
It seems that you are using react-hook-form
Checkout the example here
The onChange you are passing to the input tags are no longer needed with react-hook-form beacuse of register functionality of react-hook-form
also validations will be done react-hook-form
import React from 'react';
import { useForm } from "react-hook-form";
export default function UserForm(props) {
const { register, handleSubmit } = useForm();
const submitForm = (data) => {
console.log(data);
}
const onError = (err) => {
console.error(err);
}
return (
<div className="user-form">
<form onSubmit={handleSubmit(submitForm, onError)}>
<input
{...register("name", { //handles onChange, value, name, ref
maxLength: 35 // Your validations
})}
/>
<button>Buy</button>
</form>
</div>
)
}
Checkout docs for register
Form File Example
I'm on TS-React project, i got some inputs where scanning some barecode value. I'm using react-hook-form and the useForm Hook.
I got some little form (one input-text and one submit button) in a global form and i want to have an automation, when i press "Enter" on keyboard an action/event sends some fetch, or others.
With the and {handleSubmit} = useForm() , it work perfectly but, my input stay in focused and i need lost this focus...
So, how can i do this action ? i saw the blur() function but i didn't success to target my input from the handleSubmit function
import {Controller, useForm} from "react-hook-form"
const BasketContainer: FC = () => {
const { control, handleSubmit, setValue, watch, getValues, reset, formState: {errors}} = useForm<Basket>()
const handleScanIdSubmit = (data: any) => {
// Here my blur action
}
return (
<form onSubmit={handleSubmit(handleScanIdSubmit)}>
<Controller
render={({field: {ref, ...rest}}) => (
<InputText {...rest}
type={"text"}
label={"ID"}
errorMessage={errors.scanId.message}
/>)}
control={control}
name="scanId"
defaultValue={""}
rules={{required: "Field required"}}
/>
<Button type="submit"
/>
</form>
In advance, thanks for helps contributions :)
You can do it in a traditional way by calling blur() on an active element.
const handleScanIdSubmit = (data: any) => {
document.activeElement.blur();
}
Or you can create a reference with useRef;
import {Controller, useForm, useRef} from "react-hook-form"
const BasketContainer: FC = () => {
const controlReference = useRef(null);
const { control, handleSubmit, setValue, watch, getValues, reset, formState: {errors}} = useForm<Basket>();
const handleScanIdSubmit = (data: any) => {
controlReference.current.blur();
}
return (
<form onSubmit={handleSubmit(handleScanIdSubmit)}>
<Controller
render={({field: {ref, ...rest}}) => (
<InputText {...rest}
type={"text"}
label={"ID"}
ref={controlReference}
errorMessage={errors.scanId.message}
/>)}
control={control}
name="scanId"
defaultValue={""}
rules={{required: "Field required"}}
/>
<Button type="submit"/>
</form>
);
}
[update]
with typescript
import {useRef} from 'react';
function App() {
const controlReference = useRef(null);
const handleSubmit = async (e:React.ChangeEvent<any>) => {
e.preventDefault()
//with typescript
if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
}
// controlReference.current.blur();
}
return (
<div className="App">
<label><textarea name="reply" ></textarea></label>
<div>
<button ref={controlReference} onClick={handleSubmit}>Submit</button>
</div>
</div>
);
}
export default App;
When I submit text in the form, I want to be able to see that text in state via console.log.
I added a console.log to trigger directly after state has been added, but I don't see anything in my console, what am I doing wrong?
What I would like to see: I submit "test1" then I submit "test2", I then want to see in my console "test1, test2" in state.
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useState('');
const addToDoItem = (event) => {
setToDoItem(event.target.value), function(){
console.log(toDoItem)
}
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
You can log the change in states using a useEffect
I'd suggest making a helper function if you tend to do it often:
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
Which you'd use as follows:
useLog('toDoItem', toDoItem);
I set up an example where the toDoItem is logged as it changes and when you submit, it also logs the change in a todoItems array
const { useState, useEffect } = React;
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
function App() {
return (
<div>
<Field />
</div>
);
}
function Field() {
const [toDoItem, setToDoItem] = useState('');
useLog('toDoItem', toDoItem);
const [todos, setTodos] = useState([]);
useLog('todos', todos);
const changeTodo = (event) => {
setToDoItem(event.target.value);
};
const addTodoItem = (event) => {
event.preventDefault();
setTodos((prev) => prev.concat(toDoItem));
setToDoItem('');
};
return (
<form onSubmit={addTodoItem}>
<input type="text" value={toDoItem} onChange={changeTodo} />
<input type="submit" value="Add" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
useState doesn't provide callback after setting value. You can use useEffect instead.
React.useEffect(() => {
console.log(toDoItem);
}, [toDoItem]);
EDIT
It seems like that you want to get toDoItem value on submit. The problem is that the page is reloaded when form is submitted. You can use event.prefentDefault() to stop refreshing on form submission.
<form onSubmit={onSubmit}>
const onSubmit = (event) => {
event.preventDefault()
console.log(toDoItem)
}
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useStateWithCallback('', toDoItem => {
console.log(toDoItem);
});
const addToDoItem = (event) => {
setToDoItem(event.target.value);
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;