How to pass key:value onChange from select-option in reactjs? - reactjs

I follow this tutorial and try to modify it a little.
I have this code addRequestForm.js
import { BiPlus } from 'react-icons/bi'
import { useMutation, useQueryClient } from "react-query"
import { addRequest, getRequests } from "../../lib/helper"
import Bug from "./bugRequest"
import Success from "./successRequest"
export default function AddRequestForm({ formData, setFormData }) {
const queryClient = useQueryClient()
const addMutation = useMutation(addRequest, {
onSuccess: () => {
queryClient.prefetchQuery('requests', getRequests)
}
})
const handleSubmit = (e) => {
e.preventDefault();
if (Object.keys(formData).length == 0) return console.log("Don't have Form Data");
let { sector, contact_name } = formData;
const model = {
sector, contact_name
}
addMutation.mutate(model)
}
if (addMutation.isLoading) return <div>Loading!</div>
if (addMutation.isError) return <Bug message={addMutation.error.message}></Bug>
if (addMutation.isSuccess) return <Success message={"Added Successfully"}></Success>
return (
<form className="grid lg:grid-cols-2 w-4/6 gap-4 md:px-4 md:mx-auto" onSubmit={handleSubmit}>
<div className="input-type">
<label htmlFor="sector" className="block text-sm font-medium text-gray-700">
Sector
</label>
<select
id="sector"
name="sector"
autoComplete="sector-name"
className="border w-full px-5 py-3 focus:outline-none rounded-md"
onChange={(e) => setFormData({ [e.target.name]: e.target.value })}
>
<option value="North">North</option>
<option value="South">South</option>
<option value="West">West</option>
<option value="East">East</option>
{/* */}
</select>
</div>
<div className="form-control input-type">
<label className="input-group">
<span>Contact Name</span>
<input type="text" onChange={setFormData} name="contact_name" placeholder="Contact Name Here..“ className="w-full input input-bordered" />
</label>
</div>
<button type="submit" className="flex justify-center text-md w-2/6 bg-green-500 text-white px-4 py-2 border rounded-md hover:bg-gray-50 hover:border-green-500 hover:text-green-500">
Add <span className="px-1"><BiPlus size={24}></BiPlus></span>
</button>
</form >
)
}
For name=“contact_name” already succeed insert into database (mongodb + mongoose). But, I’m still confuse, how to do the same to select-option?
Already tried these:
onChange={(e) => setFormData({ e.target.name: e.target.value })}
onChange={(e) => setFormData({ sector.name: e.target.value })}
onChange={(e) => setFormData({ [e.target.name][0]: e.target.value })}
Got Syntax error: Unexpected token, expected ",". While I thought this is the right value when I’m consoling these ( result: “sector:myname”).
onChange={(e) => setFormData({ sector: e.target.value })}
onChange={(e) => setFormData({ [e.target.name]: e.target.value })}
No insert into database.
How to write it correctly? I’m using nextjs (i thought the same with reactjs, right?) + #reduxjs/toolkit 1.9.1.
In case needed, here is reducer.js:
import UpdateRequestForm from "./updateRequestForm";
import AddRequestForm from "./addRequestForm";
import { useSelector } from "react-redux";
import { useReducer } from "react";
const formReducer = (state, event) => {
return {
...state,
[event.target?.name]: event.target?.value
}
}
export default function FormRequest() {
const [formData, setFormData] = useReducer(formReducer, {})
const formId = useSelector((state) => state.app.client.formId)
return (
<div className="container mx-auto py-5">
{formId ? UpdateRequestForm({ formId, formData, setFormData }) : AddRequestForm({ formData, setFormData })}
</div>
)
}

try this
onChange = {(e) => {
let obj = {} ;
obj[e.target.name] = e.target.value;
setFormData(obj);
}
}

I'm not entirely following your use case for the reducer but you could replace it with a simple handleChange function as follows. Just a side note, calling setFormData(//newData) will complete erase what is in there so we first spread the original state and change the value based on the key.
State and handleChange:
const [formData, setFormData] = React.useState({
sector: "North",
contact_name: ""
});
const handleChange = ({ target }) => {
// Deconstruct the target
// Deconstruct the name and value from the target
const { name, value } = target;
// Set the state
setFormData((previousState) => ({
...previousState,
[name]: value,
}));
};
Usage:
<select
placeholder="Select a sector"
name="sector"
value={formData.sector}
onChange={handleChange}
>
<option value="North">North</option>
<option value="South">South</option>
<option value="West">West</option>
<option value="East">East</option>
</select>
<input
value={formData.contact_name}
placeholder="Contact Name"
name="contact_name"
onChange={handleChange}
/>
A codesandbox : https://codesandbox.io/s/interesting-kilby-emcyl4?file=/src/App.js:461-615
Also note that you will need to assign a name to the input element. In the example, name is set to sector. As the name value will be used to indicate which key in the state object must be updated.
It's good practice to have controlled input fields. So setting the value of the input to your state will accomplish this. Only thing to remember is you need a default state. I have added it all to the codesandbox if you would like to take a look.

In my case, just use this syntax:
…
value={formData.sector}
onChange={setFormData}
…
Case closed.

Related

How to display a popup modal in React and getForms.io

I am having a problem in displaying a popup when subbmitting a data to getForms. It executes the data but i cannot display the modal. I need help.
`
import axios from'axios'
import {React ,useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
<Modal/>//THIS DOESNT WORK!!
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted",e)
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}
`
Im expecting a popup after the data was submitted or not submitted. it do works in console.log but not in React Component.
const [serverState , setServerState] = useState({submitting: false, status: null})
const handleServerResponse = (ok, msg, form) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
//console.log() works!!!!!
//React Component not working!!!!
return
}
//Same HERE!!
return
}
How do you find out that the React code is not working? It is not visible from the code you share. I only see the Modal import, but not its usage. Try logging inside the React component to see if it is rendered and if it is, then try increasing the z-index of the modal, so you can see it.
Ok, with your Modal added to the code I see two errors here, first you don't even return the Modal from this function and second, if you return it, you don't expect it anywhere in your code. Try to render the Modal conditionally in your section instead, like this:
return(
<section>
{showModal && (
<Modal />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
...
</form>
</section>
)
Then add showModal variable into state like this:
const [showModal, setShowModal] = useState(false);
And finally change the handleServerResponce function like this:
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
You will have to add close modal logic into your Modal after that.
And here is how the whole component will look like when you add also logic for passing the message to the modal:
import axios from'axios'
import {React, useState} from 'react'
import { useForm} from 'react-hook-form'
import Modal from '../../Modal'
export default function ContactForm(){
const [serverState , setServerState] = useState({submitting: false, status: null})
const [showModal, setShowModal] = useState(false);
const handleServerResponse = (ok, msg, formData) => {
setServerState({ submitting: false, status: { ok, msg } })
if(ok){
console.log('msg')//THIS WORKS!!
setShowModal(true);
)
return
}
return
}
const { register, handleSubmit, watch, formState: { errors } } = useForm()
const onSubmit = (data,e) =>{
const formData = new FormData()
for(var field in data){
formData.append(field, data[field])
}
setServerState({submitting:true})
axios({
method:'post',
url:'https://getform.io/f/79e16760-4dbb-4e67-b212-c98e3f1b8eda',
data: formData
})
.then((r) => {
e.target.reset();
handleServerResponse(true, "Form Submitted", r);
})
.catch((r) => {
handleServerResponse(false, r.response.data.error, e);
})
}
return(
<section>
{showModal && (
<Modal message={serverState.status ? serverState.status.msg : ""} />
)}
<form
onSubmit={handleSubmit(onSubmit)}
className='mt-5 gap-4 text-black flex flex-col'
>
<input className='pl-2 rounded-sm focus:outline-none' type='text' placeholder='Enter yourName' {...register("fullname")}/>
<input className='pl-2 rounded-sm focus:outline-none' type='email' placeholder='Enter your Email' {...register("email")}/>
<textarea className='pl-2 pt-2 rounded-sm focus:outline-none' rows={10} placeholder='Enter your Message'{...register('emailMessage')}/>
<input className=' text-white drop-shadow-lg rounded-lg bg-tertiary p-2 mx-auto' type='submit'/>
</form>
</section>
)
}

Update component UI based on values obtained from SubmitForm Component using redux toolkit state management

My store.js file
import commentsReducer from "./stateSlices/commentsSlice";
export default configureStore({
reducer: {
dishes: dishesReducer,
comments: commentsReducer,
leaders: leadersReducer,
promotions: promotionsReducer,
},
});
My commentsSlice.js file
import { COMMENTS } from "../../shared/comments";
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
comments: COMMENTS,
};
export const stateSlice = createSlice({
name: "comments",
initialState,
// Updated the reducer file as below
reducers: {
addComment: {
reducer: (state = initialState.comments, action) => {
state.comments = state.comments.concat(action.payload);
},
prepare: (value) => {
return {
payload: {
...value,
date: new Date().toISOString(),
id: Math.random(),
},
};
},
},
},
});
export default stateSlice.reducer;
My dishdetail.js file
const Dishdetail = (props) => {
if (props.dish != null) {
return (
<div className="container ">
<div className="row ">
<div className="col-12 col-sm-5 col-md-5 m-1">
<RenderComments comments={props.comments} />
</div>
</div>
</div>
);
} else {
return <div></div>;
}
};
export default Dishdetail;
RenderComments component implementation
function RenderComments({ comments, commentsName }) {
// created dishId constant to get the dishId of selected dish and
//need only first index dishId and passed it as prop to
//SubmitComment component as file like this
//changed below line
const dishId = comments.map((x) => x.dishId)[0];
if (comments != null) {
return (
<>
<div>
{comments.map((comment) => {
const options = { year: "numeric", month: "short", day: "2-digit" };
return (
<div key={comment.id}>
<ul className="list-unstyled">
<li>{comment.comment}</li>
<li>
--{comment.author} {new Date(comment.date).toLocaleDateString("en-us", options)}
</li>
</ul>
</div>
);
})}
<SubmitComment dishId={dishId} />
</div>
</>
);
} else {
return <div></div>;
}
}
and My SubmitComment.js component
function SubmitComment() {
const [modal, setModal] = useState(false);
const toggle = () => setModal(!modal);
const {
register,
formState: { errors },
handleSubmit,
reset,
} = useForm();
const onSubmit = (data) => {
console.log(data);
// to get the data from input fields
const { rating, author, comment } = data;
//dispatched the addComment action, here dishId is from props
dispatch(addComment({ dishId, rating, author, comment }));
// Finally subscribed to the store by useSelector method in main
// component which is parent component of renderComments component
//which in turn is parent component of SubmitComments component
reset();
};
return (
<>
<div>
<Button color="primary" onClick={toggle}>
Submit
</Button>
<Modal isOpen={modal} toggle={toggle} fade={false}>
<ModalHeader toggle={toggle}>Submit Comment</ModalHeader>
<ModalBody>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="mb-3">
<label htmlFor="rating" className="form-label">
Rating
</label>
<select
name="rating"
className="form-select "
aria-label="Default select example"
{...register("rating")}
>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<div className="mb-3">
<label htmlFor="name" className="form-label">
Your Name
</label>
<input
type="text"
className="form-control"
id="name"
placeholder="Your Name"
name="name"
{...register("name", { required: true, maxLength: "15" })}
/>
<small className="form-text text-danger">
{errors.name?.type === "required" && "This field is required"}
{errors.name?.type === "maxLength" && "Maximum 15 characters are allowed"}
</small>
</div>
<div className="mb-3">
<label htmlFor="comment" className="form-label">
Comment
</label>
<textarea
type="text"
name="comment"
className="form-control"
id="comment"
aria-describedby="comment"
rows="6"
{...register("comment")}
/>
</div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</form>
</ModalBody>
</Modal>
</div>
</>
);
}
export default SubmitComment;
I am expecting that once I click on the submitcomment form, all the values that I entered in the fields namely rating, author and comments should get added in the renderComment component.
like this
I have tried to add dispatch action after clicking on submit in form and tried to use it in rendercomments component file using useSelector but i am unable to do it
So if anyone can describe the redux flow and basic working flow from here so I could implement this functionality
1 Created reducer in commentSlice.js file
2 In Dishdetail component made following changes:
In renderComments function
created dishId constant to get the dishId of selected dish and
need only first index dishId and passed it as prop to
SubmitComment component which is child component of renderComments
3 Received dishId as prop in SubmitComment and passed it as payload along with other valued received from the user input fields to the action addComment using dispatch
4 Finally subscribed the new state in Main component using useSelectore and passed as prop to Dishdetail component which is child component of main component
Successfully achieved the desired functionality.
I have updated the question with the changes.

Download URL from storage not transferring to firestore - React [duplicate]

This question already has answers here:
The useState set method is not reflecting a change immediately
(15 answers)
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 7 months ago.
I am trying to add the imgUrl uploaded to firestore but it's not working. The url is generated and the image is stored in firebase storage but I can't seem to pass that URL as a field into Firestore
This is my code
import React, {useEffect, useState} from "react";
import ReactTagInput from "#pathofdev/react-tag-input";
import "#pathofdev/react-tag-input/build/index.css";
import {db, storage} from "../firebase";
import { getDownloadURL, ref, uploadBytes, listAll} from "firebase/storage";
import {
addDoc,
collection,
getDoc,
serverTimestamp,
doc,
updateDoc,
} from "firebase/firestore";
import { toast } from "react-toastify";
import { v4 } from 'uuid';
import {useNavigate} from "react-router-dom";
const initialState = { // initial state for our form
title: "",
tags: [],
trending: "no",
category: "",
description: "",
imgUrl: ""
}
const categoryOption = [
"Fashion",
"Technology",
"Food",
"Politics",
"Sports",
"Business"
]
export default function AddEditBlog({ user }) {
let urlDownload
const [form, setForm] = useState(initialState)
const [imageUpload, setImageUpload] = useState()
const [imageList, setImageList] = useState([])
const navigate = useNavigate()
const imageListRef = ref(storage, "images/") // to use in useEffect (listAll), because we
// wanna access all the files inside the images folder
const { title, tags, category, trending, description, imgUrl} = form;
const uploadImage = () => {
if(imageUpload == null) return; // if there's no image, return and get out of this function
uploadBytes(imageRef, imageUpload).then((snapshot)=> {
getDownloadURL(snapshot.ref).then((url) => {
// setImageList((prev) => [...prev, url])
console.log("url", url)
toast.info("Image upload to firebase successfully");
setForm((prev) => ({ ...prev, imgUrl: url }))
console.log("form", form)
})
})
}
const handleChange = (event) => {
setForm({...form, [event.target.name]: event.target.value})
}
const handleTags = (tags) => {
setForm({ ...form, tags})
}
const handleTrending = (event) => {
setForm({ ...form, trending: event.target.value })
}
const onCategoryChange = (event) => {
setForm({ ...form, category: event.target.value })
}
const handleSubmit = async (event) => {
event.preventDefault()
if (category && tags && title && description && trending && imgUrl) {
try {
console.log("url", urlDownload)
await addDoc(collection(db, "blogs"), {
...form,
timestamp: serverTimestamp(),
author: user.displayName,
userId: user.uid
})
toast.success("Blog created successfully");
} catch(error) {
console.log(error)
}
} else {
console.log("complete form")
}
navigate("/")
}
return(
<div className="container-fluid mb-4">
<div className="container">
<div className="col-12">
<div className="text-center heading py-2">
Create Blog
</div>
</div>
<div className="row h-100 justify-content-center align-items-center">
<div className="col-10 col-md-8 col-lg-6">
<form className="row blog-form" onSubmit={handleSubmit}>
<div className="col-12 py-3">
<input
type="text"
className="form-control input-text-box"
placeholder="Title"
name="title"
value={title}
onChange={handleChange}
/>
</div>
<div className="col-12 py-3">
<ReactTagInput
tags={tags}
placeholder="Tags"
onChange={handleTags}
/>
</div>
<div className="col-12 py-3">
<p className="trending">Is it trending blog ?</p>
<div className="form-check-inline mx-2">
<input
type="radio"
className="form-check-input"
value="yes"
name="radioOption"
checked={trending === "yes"} //if trending is yes select checked button
onChange={handleTrending}
/>
<label htmlFor="radioOption" className="form-check-label">
Yes
</label>
<input
type="radio"
className="form-check-input"
value="no"
name="radioOption"
checked={trending === "no"} //if trending is not select checked button
onChange={handleTrending}
/>
<label htmlFor="radioOption" className="form-check-label">
No
</label>
</div>
</div>
<div className="col-12 py-3">
<select
value={category}
onChange={onCategoryChange}
className="catg-dropdown"
>
<option>Please select category</option>
{categoryOption.map((option, index) => (
<option value={option || ""} key={index}>
{option}
</option>
))}
</select>
</div>
<div className="col-12 py-3">
<textarea
className="form-control description-box"
placeholder="Description"
value={description}
name="description"
onChange={handleChange}
/>
</div>
<div className="mb-3">
<input
type="file"
className="form-control"
onChange={(event)=> {
setImageUpload(event.target.files[0])
}}
/>
</div>
<div className="col-12 py-3 text-center">
<button
className="btn btn-add"
type="submit"
onClick={uploadImage}
>
Submit
</button>
</div>
</form>
</div>
</div>
</div>
</div>
)
}
Everything else loads successfully. The console log I do of the url returns the correct URL so I don't know why I can't pass it into firestore

React-Apollo-Hooks useMutation passing empty variables?

I am trying to create an addBook mutation, but when I submit the form and call the mutation, a book is created with empty strings as the name, the genre and the authorID.
I can run the mutation in GraphiQL and it works correctly so I think that means the problem is in the component.
I'm not sure what is wrong?
Mutation:
const addBookMutation = gql`
mutation {
addBook(name: "", genre: "", authorID: "") {
id
name
}
}
`;
Component:
import React, { useState } from 'react';
import { useQuery, useMutation } from '#apollo/react-hooks';
import { getAuthorsQuery, addBookMutation } from '../queries/queries';
export default function AddBook() {
const { loading, error, data } = useQuery(getAuthorsQuery);
const [name, setName] = useState('');
const [genre, setGenre] = useState('');
const [authorID, setAuthorID] = useState('');
const [addBook, { data: bookData }] = useMutation(addBookMutation);
let authors = [];
if (error) return <p>Error :(</p>;
if (data) {
authors = data.authors;
}
return (
<div>
<form
className="bg-blue-900 p-4 flex flex-1 flex-wrap"
onSubmit={e => {
e.preventDefault();
addBook({
bookData: { name, genre, authorID },
});
}}
>
<label className="font-semibold text-gray-100">Book Name</label>
<input
type="text"
className="py-1 px-2 rounded border mx-2"
onChange={e => setName(e.target.value)}
/>
<label className="font-semibold text-gray-100">Genre</label>
<input
type="text"
className="py-1 px-2 rounded border mx-2"
onChange={e => setGenre(e.target.value)}
/>
<label className="font-semibold text-gray-100">Author</label>
<select
className="py-1 px-2 rounded border mx-2"
onChange={e => setAuthorID(e.target.value)}
>
{loading ? <option>Loading authors...</option> : ''}
<option defaultValue>Select Author</option>
{authors.map(author => (
<option key={author.id} value={author.id}>
{author.name}
</option>
))}
</select>
<button
type="submit"
className="bg-gray-200 text-gray-900 font-semibold py-1 px-2 rounded"
>
Add Book
</button>
</form>
</div>
);
}
First add the variables declaration to mutation
const addBookMutation = gql`
mutation AddBook($name: String!, $genre: String!, $authorID: String!){
addBook(name: $name, genre: $genre, authorID: $authorID) {
id
name
}
}
`;
Then pass the variables to the mutation as are declared
...
e.preventDefault();
addBook({
variables: { name, genre, authorID },
});
...

Page not re-rendering after hook state update

The page renders the input correctly, however errors only seem to appear on the second render even though they are displayed is useForm state on the first render.
So for the password field I enter a single character and the state of useForm changes to {password: "Must be at.....} but the screen does not update to display errors.password until I enter in another character.
// nodejs library that concatenates classes
// reactstrap components
import {
Button,
Card,
CardBody,
CardHeader,
Col,
Container,
Form,
FormFeedback,
FormGroup,
Input,
Row
} from "reactstrap";
import {register} from "apiCalls/AuthRequests";
import useForm from "hooks/AuthHooks";
import {registerFormValidation} from "components/validation/AuthFormValidation";
function RegisterForm(props) {
const [loading, setLoading] = useState(false);
const {values, handleChange, handleSubmit, errors, setErrors} = useForm(submit, registerFormValidation);
const emailRef = useRef(null);
const passwordRef = useRef(null);
const accessCodeRef = useRef(null);
async function submit() {
setLoading(true);
const response = await register(values.email, values.password, values.accessCode);
if (response.ok) {
} else if (response.status === 422) {
if ("access_code" in response.data) {
accessCodeRef.current.focus();
setErrors({accessCode: response.data.access_code});
}
if ("email" in response.data) {
setErrors({email: response.data.email});
emailRef.current.focus();
}
if ("password" in response.data) {
setErrors({password: response.data.password});
passwordRef.current.focus();
}
}
setLoading(false)
}
useEffect(() => {
console.log(errors);
});
return (
<>
<div className="content">
<Container className="pb-5">
<Row>
<Col lg="6" md="8" className="ml-auto mr-auto">
<Card className="bg-secondary border-0">
<CardHeader className="bg-transparent">
<div className="text-center">
<h2>Register</h2>
</div>
</CardHeader>
<CardBody className="px-lg-5 py-lg-5">
<Form role="form" onSubmit={handleSubmit}>
<FormGroup>
<label
className="form-control-label"
>
Email
</label>
<Input
name="email"
type="email"
innerRef={emailRef}
value={values.email || ""}
onChange={handleChange}
invalid={!!errors.email}
required
/>
<FormFeedback>{errors.email}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Password
</label>
<Input
name="password"
type="password"
innerRef={passwordRef}
value={values.password || ""}
onChange={handleChange}
invalid={!!errors.password}
required
/>
<FormFeedback>{errors.password}</FormFeedback>
</FormGroup>
<FormGroup>
<label
className="form-control-label"
>
Access Code
</label>
<Input
name="accessCode"
type="text"
innerRef={accessCodeRef}
value={values.accessCode || ''}
onChange={handleChange}
invalid={!!errors.accessCode}
required
/>
<FormFeedback>{errors.accessCode}</FormFeedback>
</FormGroup>
<Row className="my-4">
<Col xs="12">
<div
className="custom-control custom-control-alternative custom-checkbox">
<input
className="custom-control-input"
id="customCheckRegister"
type="checkbox"
required
/>
<label
className="custom-control-label"
htmlFor="customCheckRegister"
>
<span className="text-muted">
I agree with the{" "}
<a
href=""
target="_blank"
rel="noopener noreferrer"
>
Privacy Policy
</a>
</span>
</label>
</div>
</Col>
</Row>
<div className="text-center">
<Button disabled={loading} className="mt-4" color="info" type="submit">
Create account
</Button>
</div>
</Form>
</CardBody>
</Card>
</Col>
<Col md="4" className="ml-auto mr-auto">
<h2>Being a photographer is easier with <b className="text-primary">FOCAL</b></h2>
<ul>
<li>
<h4>Focal is great</h4>
</li>
<li>
<h4>Save time</h4>
</li>
<li>
<h4>More customers</h4>
</li>
</ul>
</Col>
</Row>
</Container>
</div>
</>
);
}
export default RegisterForm;
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = (event) => {
if (event) event.preventDefault();
setIsSubmitting(true);
};
useEffect(() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
setIsSubmitting(false);
}
}, [callback, errors, isSubmitting]);
useEffect(() => {
setErrors(validate(values, errors));
}, [validate, values, errors]);
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate(values, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
export function registerFormValidation(values, errors) {
if (values.email === ""){
delete errors.email;
}
if (values.password) {
if (!verifyLength(values.password, PASSWORD_LENGTH)){
errors.password = "Password must be greater than 8 Characters";
} else {
delete errors.password;
}
}
if (values.accessCode === "") {
delete values.accessCode;
}
return errors
}```
I can appreciate that it's of interest to work on a custom hook but forms are ubiquitous and there are solid, proven means to work with them. IMHO Formik is probably the best.
With the three fields that you have there, you could implement something like the following:
import React from 'react';
import { Formik } from 'formik';
const BasicExample = () => (
<div>
<Formik
initialValues={{ email: '', password: '', accessCode: '' }}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
}}
render={props => (
<form onSubmit={props.handleSubmit}>
<input
type="email"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.email}
name="email"
/>
{props.errors.email && <div id="feedback">{props.errors.email}</div>}
<input
type="password"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.password}
name="password"
/>
{props.errors.password && <div id="feedback">{props.errors.password}</div>}
<input
type="text"
onChange={props.handleChange}
onBlur={props.handleBlur}
value={props.values.accessCode}
name="accessCode"
/>
{props.errors.accessCode && <div id="feedback">{props.errors.acessCode}</div>}
<button type="submit">Submit</button>
</form>
)}
/>
</div>
);
Note that the above is just from memory - which to some extent speaks to it's simplicity. As initialValues are supplied to Formik, you can build complex component hierarchies without having to pass down form state but that's just one of the various benefits.
I have an assumption.
In registerFormValidation doesn't modify errors object. Create a new each time.
FIXED:
const useForm = (callback, validate) => {
const [values, setValues] = useState({});
const [errors, setErrors] = useState({});
const handleSubmit = (event) => {
if (event) event.preventDefault();
if (Object.keys(errors).length === 0){
callback();
}
};
const handleChange = (event) => {
event.persist();
setValues(values => ({...values, [event.target.name]: event.target.value}));
setErrors(validate({[event.target.name]: event.target.value}, errors));
};
return {
handleChange,
handleSubmit,
setErrors,
values,
errors
}
};
export default useForm;
got rid of useEffect!

Resources