I'm trying to validate a simple form with a single input just for practice. I also don't want the value that the user types in the input to disappear after page refresh, for that reason, I did a little bit of searching and found out about saving that data using localStorage. After trying to implement that for a while, I managed to do that, when I refresh the page, the value is still there. However, now, when I'm trying to validate the form using useForm from react-hook-form, It just doesn't work for some reason, when I try to use that same useForm logic with an input without using localStorage, It works just fine, but while trying to add localStorage functionality, then it doesn't. I hope I'm describing my problem at least okey, here's the code :
import React, {useEffect, useState } from "react";
import "./App.css"
import { useForm } from "react-hook-form";
const getForm = () => {
const storedValues = localStorage.getItem("form");
if(!storedValues) return {
name: "",
age: ""
}
return JSON.parse(storedValues);
}
function Home() {
const [values, setValues] = useState(getForm)
const {register, handleSubmit, watch} = useForm();
const handleChange = (e) => {
setValues((previousValues) => ({
...previousValues,
[e.target.name]: e.target.value,
}))
}
const onSubmit = async data => { console.log(data); };
useEffect(()=>{
localStorage.setItem("form", JSON.stringify(values))
}, [values])
return (
<div className="container">
<form onSubmit={handleSubmit(onSubmit)}>
<input value={values.name} onChange={handleChange} name="name" placeholder="name" />
<input value={values.age} onChange={handleChange} name="age" placeholder="age"/>
<button type="submit">Submit</button>
</form>
</div>
)
}
export default Home;
This code works fine since I'm not adding useForm register to the input, but if I do that, then It gets buggy, like this :
<input value={values.name} onChange={handleChange} name="name" placeholder="name" {...register("name")} />
The latest code only works If I remove the value atrribute from the input, but I can't do that, If I do, I can't use localStorage anymore.
Looking at the documentation, you had the syntax a little off with your register function. That function takes a second argument, which is an object of props, and that is where you want to define value, name and onChange.
Like this:
<input
placeholder="name"
{...register("name", {
onChange: handleChange,
name: "name",
value: values.name
})}
/>
Here is the full code I have working on a codesandbox. That's really all I changed, expect removing the watch import.
import React, { useEffect, useState } from "react";
import "./styles.css";
import { useForm } from "react-hook-form";
const getForm = () => {
const storedValues = localStorage.getItem("form");
if (!storedValues)
return {
name: "",
age: ""
};
return JSON.parse(storedValues);
};
function Home() {
const [values, setValues] = useState(getForm);
const { register, handleSubmit } = useForm();
const handleChange = (e) => {
setValues((previousValues) => ({
...previousValues,
[e.target.name]: e.target.value
}));
};
const onSubmit = async (data) => {
console.log(data);
};
useEffect(() => {
localStorage.setItem("form", JSON.stringify(values));
}, [values]);
return (
<div className="container">
<form onSubmit={handleSubmit(onSubmit)}>
<input
placeholder="name"
{...register("name", {
onChange: handleChange,
name: "name",
value: values.name
})}
/>
<input
value={values.age}
onChange={handleChange}
name="age"
placeholder="age"
/>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default Home;
Related
I'm trying to store user input in local storage and had it functioning. But because I will need to test it, I have changed my code and made a function that will work as a custom hook, that I will call in my tests also.
Now the page is rendering but I am not able to type into the input box?
When hovered over the box the mouse cursor doesn't even respond as if it isn't an input field.
I believe the problem lies in my useStateWithLocalStorage function:
import { useState, useEffect } from 'react';
const useStateWithLocalStorage = (defaultValue, key) => {
const [value, setValue] = useState(() => {
const storedValues = localStorage.getItem(key);
return storedValues !== '' ? JSON.parse(storedValues) : defaultValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
export default useStateWithLocalStorage;
In particular this line
return storedValues !== '' ? JSON.parse(storedValues) : defaultValue;
The value before anything is parsed into the localstorage should be name: ''
Here is my component:
import React from 'react';
import { Container, Title } from '#mantine/core';
import useStateWithLocalStorage from './Handlers';
const UserForm = () => {
const [inputValue, setInputValue] = useStateWithLocalStorage('', 'form');
function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
}
function handleChange(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) {
setInputValue((previousValues) => ({
...previousValues,
[event.target.name]: event.target.value,
}));
}
return (
<Container>
<Title order={2}>Welcome {inputValue.name}</Title>
<form onSubmit={handleSubmit}>
<label htmlFor="name">
Name
<input
type="text"
name="name"
id="name"
placeholder="enter your name"
onChange={handleChange}
value={inputValue.name}
/>
</label>
</form>
</Container>
);
};
export default UserForm;
I hope I've explained myself well enough and haven't wasted anyone's time. I'd be thankful for any help.
I have a form that have an onSubmit, in that callback I have a uploady.showFileUpload(), but the code after uploady.showFileUpload() is executed.
Now the question is how can I wait for uploady, and then execute the rest of the code?
const handleSubmit2 = useCallback((e)=> {
uploady.showFileUpload(); //(HERE SHOULD WAIT FOR IT TO FINISH FILE SELECT)
//OTHER CODE
});
This codesandbox should be helpful in the case of using Uploady with a form:
https://codesandbox.io/s/react-uploady-inside-form-ys1wx
The idea is that you show the file selection prompt separately from submitting the form:
import React, { useState, useCallback, useMemo, forwardRef } from "react";
import Uploady, {
useUploadyContext
} from "#rpldy/uploady";
import { asUploadButton } from "#rpldy/upload-button";
const MyUploadField = asUploadButton(
forwardRef(({ onChange, ...props }, ref) => {
return (
<div {...props} ref={ref} id="form-upload-button" title={text}>
Select file
</div>
);
})
);
const MyForm = () => {
const [fields, setFields] = useState({});
const [fileName, setFileName] = useState(null);
const uploadyContext = useUploadyContext();
const onSubmit = useCallback(() => {
uploadyContext.processPending({ params: fields });
}, [fields, uploadyContext]);
const onFieldChange = useCallback(
(e) => {
setFields({
...fields,
[e.currentTarget.id]: e.currentTarget.value
});
},
[fields, setFields]
);
return (
<form>
<MyUploadField autoUpload={false} />
<input
onChange={onFieldChange}
id="field-name"
type="text"
placeholder="your name"
/>
<SubmitButton
id="form-submit"
type="button"
onClick={onSubmit}
>
Submit Form
</SubmitButton>
</form>
);
};
<Uploady
clearPendingOnAdd
destination={{ url: "[upload-url]" }}
multiple={false}
>
<MyForm />
</Uploady>
Selection is achieved by using the asUploadButton HOC. You can of course do so yourself as you did with uploady.showFileUpload();.
Then, the submit button uses uploady's processPending method to start uploading.
I am getting "is not a function" error for a defined prop in my React application.
I tried adding onAdd={() => addTodo()}, however, it did not work.
I would like to know why the error is occurring and how to fix the error.
Please see the error picture
TodoForm.js - where I accept my props
import React, {useState} from 'react';
const TodoForm = (props) => {
const [input, setInput] = useState('');
const handleSubmit = e => {
// console.log(e);
e.preventDefault();
props.onAdd({
id: Math.floor(Math.random() * 10000),
text: input
});
setInput('');
};
return(
<div>
<form onSubmit={handleSubmit}>
<input type="text"
placeholder="Add a todo"
value={input} name="text"
className='todo-input'
onChange={e => setInput(e.target.value)} />
<input type="submit" value="Add todo"></input>
</form>
</div>
);
}
export default TodoForm;
TodoList.js
import React, { useState } from 'react';
import TodoForm from './TodoForm';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if(!todo.text || /^\s*$/.test(todo.text)){
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(...todos);
console.log("is working");
}
return(
<div>
<h1>Waht's the plan for today?</h1>
<TodoForm onAdd={() => addTodo()} />
</div>
);
}
export default TodoList;
Make sure you save the file , something is wrong , you code should still work , not how you want it to work but it should work , however the solution provided by Sean is 100% correct .
"onAdd={() => addTodo()}, however, it did not work." here you simply create a function that return a function.
The state is updated only on the next keystroke but with the previous state. Screen 1
When you click on updateForm (), it is also empty, only after the second click, the state is updated. Screen 2
I understand that this is due to asynchrony, but in this case I do not know how to use it.
Home.jsx
import React, { useState } from 'react';
import { Form } from '../components/Form/Form';
const Home = () => {
const [dateForm, setDataForm] = useState({});
const updateForm = eachEnry => {
setDataForm(eachEnry);
console.log(dateForm);
};
return (
<div>
<Form updateForm={updateForm} />
</div>
);
};
export default Home;
Form.jsx
import React, { useState } from 'react';
import './Form.scss';
export const Form = ({ updateForm }) => {
const initInputState = {
name: '',
password: ''
};
const [dataForm, setDataForm] = useState(initInputState);
const { name, password } = dataForm;
const onChange = e => {
setDataForm({
...dataForm,
[e.target.name]: e.target.value
});
};
const onSubmit = e => {
e.preventDefault();
updateForm(dataForm);
};
return (
<div>
<form onSubmit={onSubmit}>
<input
type="text"
placeholder="Name"
value={name}
onChange={onChange}
name="name"
/>
<input
placeholder="Password"
onChange={onChange}
value={password}
type="text"
name="password"
/>
<button type="submit">Login</button>
</form>
</div>
);
};
Your code is working fine. You just doing console.log before the state is updated. State updates happen not when you using an update state function. It's happening when all component action and nested components actions are done.
Check your code with console log on another place click to check
As you can see I placed a console log on every Home component rerender. You can check that all works fine.
P.S. I did some improvements to your code. Check if u like it. And add a comment to updateForm function. Check this one too, please.
You evidently are not setting your state properly, here
setDataForm({
...dataForm,
[e.target.name]: e.target.value
});
should be
setDataForm(c => ({
...c,
[e.target.name]: e.target.value
}));
I'm trying to implement a login function to my react app.
import React, { useState, useEffect } from 'react'
import { useQuery, useLazyQuery, useMutation } from "#apollo/client"
import { useForm } from "react-hook-form"
import { LOGIN } from '../queries/queries'
const Login = () => {
const [formValue, setFormValue] = useState({})
const { loading, error, data } = useQuery(LOGIN, {
variables: {
email: formValue.email,
password: formValue.password
}
})
const { register, handleSubmit } = useForm()
const onSubmit = (value) => {
setFormValue(value)
}
if (loading) return <p>loading</p>
return(
<>
<form onSubmit={handleSubmit(onSubmit)} >
<input
type="text"
name="email"
placeholder="E-mail"
ref={register}
/>
<input
type="password"
name="password"
placeholder="Password"
ref={register}
/>
<button type="submit">
Login
</button>
</form>
</>
)
}
When I code console.log(data.user) for example, error happens because user is not undefined.
I know I can get object from data if I code variables directly, but I want to get it after handleSubmit.
I think if I can make data object initially, error would not happen.
Then is there any way to do that?
try "data?.user" instead of "data.user" when referring to that object attribute
the question mark should disable the error if the object doesnt exist
update:
you can also try declaring data as an empty objec literal:
{ loading, error, data = {} }