How do I make the invalid hook call go away? - reactjs

I get this error when trying to npm start my project:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
import React, { useState, useEffect } from 'react';
import './index.css';
import conspireLogo from './conspireLogo.png';
import Post from './Post'
import { auth, db } from './firebase';
import { makeStyles } from '#material-ui/core/styles';
import Modal from '#material-ui/core/Modal';
import { Button, Input } from '#material-ui/core'
import ImageUpload from './imageUpload';
//import { BrowserRouter, Route, Switch } from 'react-router-dom';
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: 'absolute',
width: 400,
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function MainPage() {
const classes = useStyles();
const [modalStyle] = useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
const [openSignIn, setOpenSignIn] = useState(false);
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((authUser) => {
if (authUser) {
console.log(authUser);
setUser(authUser);
} else {
setUser(null);
}
})
return () => {
unsubscribe();
}
}, [user,username]);
useEffect(() => {
db.collection('posts').orderBy('timestamp', 'desc').onSnapshot(snapshot => {
setPosts(snapshot.docs.map(doc => ({
id: doc.id,
post: doc.data()
})));
})
}, []);
const signUp = (event) => {
event.preventDefault();
auth
.createUserWithEmailAndPassword(email, password)
.then((authUser) => {
authUser.user.updateProfile({
displayName: username
})
})
.catch((error) => alert(error.message));
}
const signIn = (event) => {
event.preventDefault();
auth
.signInWithEmailAndPassword(email, password)
.catch((error) => alert(error.message));
setOpenSignIn(false);
}
return (
<div className="app">
<Modal
open={open}
onClose={() => setOpen(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
</center>
<Input
placeholder="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signUp}>Sign Up</Button>
</form>
</div>
</Modal>
<Modal
open={openSignIn}
onClose={() => setOpenSignIn(false)}
>
<div style={modalStyle} className={classes.paper}>
<form className="app__signup">
<center>
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
</center>
<Input
placeholder="email"
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Button type="submit" onClick={signIn}>Sign In</Button>
</form>
</div>
</Modal>
<div className="app__header">
<img
className="app__headerImage"
src={conspireLogo}
alt="Conspire Logo"
/>
{user ? (
<Button onClick={() => auth.signOut()}>Logout</Button>
): (
<div className="app__loginContainer">
<Button onClick={() => setOpenSignIn(true)}>Sign In</Button>
<Button onClick={() => setOpen(true)}>Sign Up</Button>
</div>
)}
</div>
<div className="app__footer">
<Button onClick={() => setOpenSignIn(true)}><img
className="app__footerImage"
src="http://www.simpleimageresizer.com/_uploads/photos/bdfbb0d1/346a1f4363e1b59f6860fdce6abc1082_2_15.jpg"
alt="Create"
/>
</Button>
</div>
<div className="app__posts">
<div className="app__postsLeft">
{
posts.map(({id, post}) => (
<Post key={id} postId={id} user={user} username={post.username} caption={post.caption} imageUrl={post.imageUrl}></Post>
))
}
</div>
</div>
{user?.displayName ? (
<ImageUpload username={user.displayName} />
): (
<h3 className="center">Sorry you need to login to upload</h3>
)}
</div>
);
}
export default MainPage;

const [modalStyle] = useState(getModalStyle);
You're storing the function reference, not the function's return. Change it to
const [modalStyle] = useState(getModalStyle());
Moreover as you don't need to change this modalStyle, you don't need to have it in state
<div style={getModalStyle()} className={classes.paper}>

I don't think so there should be any error in this file, please check the other files
ex - Post, ImageUpload

Related

Directus and React data not submitting to API

I'm trying to submit reviews to a directus app but the review is not getting added to the data.
I can retrieve the data but cannot add.
I've checked all permissions and set the directus app to public permissions.
cannot figure out what the problem could be.
can anyone advise what could be wrong?
the api call:
import axios from 'axios';
const url = 'https://pwsbbqhj.directus.app/items/Reviews';
export const readReviews = () => axios.get(url);
export const createReview = (newReview) => axios.post(url, newReview);
the data retrieval :
import React, { useState, useEffect } from 'react';
import * as api from '../../../api';
import { FaStar } from 'react-icons/fa';
const colors = {
orange: '#e42320',
grey: '#a9a9a9',
};
function Ratings() {
const stars = Array(5).fill(0);
const [currentValue, setCurrentValue] = React.useState(0);
const [hoverValue, setHoverValue] = React.useState(undefined);
const handleClick = (value) => {
setCurrentValue(value);
};
const handleMouseOver = (value) => {
setHoverValue(value);
};
const [review, setReview] = useState({});
const [reviews, setReviews] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await api.readReviews();
setReviews(result.data.data);
};
fetchData();
}, []);
const createReview = async (review) => {
try {
const data = await api.createReview({ review });
setReview([...reviews, data.data]);
} catch (error) {
console.log(error);
}
};
let [reviewCount, setreviewCount] = useState([]);
const setCountFxn = (no) => {
setReview(no);
console.log(no);
};
return (
<div className='col-md-12 row-testimonials'>
<div className='reviews-heading pb-5 pt-5'>
<h2>REVIEWS FROM OUR CUSTOMERS</h2>
</div>
<div
className='themesflat-carousel-box has-bullets bullet-circle has-buttons has-arrows clearfix'
data-gap={30}
data-column={3}
data-column2={2}
data-column3={1}
data-auto='false'
>
<div className='owl-carousel owl-theme'>
{reviews.map((review) => (
<div className='themesflat-testimonials style-2 align-center clearfix' key={review.id}>
<div className='testimonial-item'>
<div className='inner'>
<div className='thumb'>
<img src='assets/img/testimonials/customer-1-90x90.jpg' alt='altimage' />
</div>
<blockquote className='text'>
<div className='name-pos clearfix'>
<h6 className='name'>{review.Title}</h6>
<span className='position'></span>
</div>
<p>{review.Body}</p>
<div className='list-star'>
{Array.from({ length: review.Rating }).map((i) => (
<FaStar key={i} size={18} color={colors.orange} />
))}
</div>
<div className='m-2'>
By: <span className='name'>{review.Name}</span>
</div>
</blockquote>
</div>
</div>
</div>
))}
</div>
</div>
<div className='bg-black'>
<form>
<div className='mb-5'>
<h2>RATE OUR SERVICE</h2>
<div className='mt-5 mb-5'>
{stars.map((_, index) => {
return (
<FaStar
key={index}
size={20}
style={{
marginRight: 10,
cursor: 'pointer',
}}
color={(hoverValue || currentValue) > index ? colors.orange : colors.grey}
onClick={() => {
setReview({ ...review, Rating: index + 1 });
}}
onMouseOver={() => handleMouseOver(index + 1)}
/>
);
})}
</div>
<div id='message'></div>
<div>
<input
type='text'
placeholder='Your Name'
required
value={review.Name}
onChange={(e) => setReview({ ...review, Name: e.target.value })}
className='wpcf7-form-control'
/>
</div>
<div>
<input
type='text'
placeholder='Review Title'
required
value={review.Title}
onChange={(e) => setReview({ ...review, Title: e.target.value })}
className='wpcf7-form-control'
/>
</div>
<textarea
placeholder='Your comment'
required
value={review.Body}
onChange={(e) => setReview({ ...review, Body: e.target.value })}
className='wpcf7-form-control'
/>
<button type='submit' className='submit wpcf7-form-control wpcf7-submit' onClick={createReview}>
submit
</button>
</div>
</form>
</div>
<div className='themesflat-spacer clearfix' data-desktop={80} data-mobile={60} data-smobile={60} />
</div>
);
}
export default Ratings;

Why is my onSubmit not working in PrimeReact Dialog?

My code is no longer submitting data when onSubmit is pushed even though it was when I was using a and tag:
<Modal><Form onSubmit={ saveProject }></Form></Modal>
The only thing different now is I substituted Modal for as I am using PrimeReact and deleted the tag. Now it's no longer submitting data.
Can anyone tell me why it is no longer submitting the data to the backend?
/Projects.js
import React, { useState, useEffect } from "react";
import { Column } from "primereact/column";
import { DataTable } from "primereact/datatable";
import { Button } from "primereact/button";
// import { Modal, ModalFooter, ModalHeader } from "react-bootstrap";
import { InputText } from "primereact/inputtext";
import { InputTextarea } from "primereact/inputtextarea";
// import { InputNumber } from "primereact/inputnumber";
import { Dropdown } from "primereact/dropdown";
import { Dialog } from "primereact/dialog";
import axios from "axios";
const Projectz = () => {
const [ticket_title, setTicketTitle] = useState("");
const [ticket_description, setTicketDescription] = useState("");
// const [time_takes, setTimeTakes] = useState("");
const [type_menu, setTypeMenu] = useState("");
const [priority_menu, setPriorityMenu] = useState("");
const [status_menu, setStatusMenu] = useState("");
const [projects, setProjects] = useState([]);
//part of modal
const [displayResponsive, setDisplayResponsive] = useState(false);
// const [position, setPosition] = useState("center");
useEffect(() => {
getProjects();
}, []);
const getProjects = async () => {
const response = await axios.get("http://localhost:5002/ticket_table");
setProjects(response.data);
};
const saveProject = async (e) => {
e.preventDefault();
await axios.post("http://localhost:5002/ticket_table", {
ticket_title: ticket_title,
ticket_description: ticket_description,
// time_takes: time_takes,
type_menu: type_menu,
priority_menu: priority_menu,
status_menu: status_menu,
});
};
const dropdownValues1 = [{ value: "Issue" }, { value: "Bug" }, { value: "Error" }, { value: "Other" }];
const dropdownValues2 = [{ value: "Low" }, { value: "Medium" }, { value: "High" }, { value: "Immediate" }];
const dropdownValues3 = [{ value: "New" }, { value: "Open" }, { value: "In Progress" }, { value: "Resolved" }, { value: "Additional Info Required" }];
const dialogFuncMap = {
displayResponsive: setDisplayResponsive,
};
const onClick = (name, position) => {
dialogFuncMap[`${name}`](true);
};
const onHide = (name) => {
dialogFuncMap[`${name}`](false);
};
const renderFooter = (name) => {
return (
<div>
<Button label="Submit" className="p-button-rounded p-button-success mr-2 mb-2 success" />
</div>
);
};
// const [show, setShow] = useState(false);
// const handleClose = () => setShow(false);
// const handleShow = () => setShow(true);
return (
<>
<div className="grid table-demo">
<div className="col-12">
<div className="card">
<h5>Tickets</h5>
<div>
{/* <Button label="New Ticket" className="p-button-rounded mr-2 mb-2 npbutton" onClick={handleShow} /> */}
<Button className="p-button-rounded mr-2 mb-2 npbutton" label="New Ticket" onClick={() => onClick("displayResponsive")} />
</div>
<Dialog className="dialogModal" header="Create Ticket" visible={displayResponsive} onHide={() => onHide("displayResponsive")} breakpoints={{ "960px": "75vw" }} style={{ width: "30vw" }} footer={renderFooter("displayResponsive")}>
<form onSubmit={saveProject}>
<h5>Ticket Name</h5>
<InputText value={ticket_title} onChange={(e) => setTicketTitle(e.target.value)} type="text" placeholder="Enter ticket name"></InputText>
<h5>Ticket Description</h5>
<InputTextarea value={ticket_description} onChange={(e) => setTicketDescription(e.target.value)} type="text" placeholder="Enter ticket description" autoResize rows="3" cols="30" />
{/* <h5>Time Estimate (Hours)</h5> */}
{/* <InputNumber value={time_takes} onValueChange={(e) => setTimeTakes(e.value)} showButtons mode="decimal"></InputNumber> */}
<h5>Type</h5>
<Dropdown value={type_menu} onChange={(e) => setTypeMenu(e.value)} options={dropdownValues1} optionLabel="value" placeholder="Select" />
<h5>Priority</h5>
<Dropdown value={priority_menu} onChange={(e) => setPriorityMenu(e.value)} options={dropdownValues2} optionLabel="value" placeholder="Select" />
<h5>Status</h5>
<Dropdown value={status_menu} onChange={(e) => setStatusMenu(e.value)} options={dropdownValues3} optionLabel="value" placeholder="Select" />
</form>
</Dialog>
<div>
<DataTable
// sortMode="single" sortField="representative.name"
sortOrder={1}
scrollable
scrollHeight="400px"
responsiveLayout="scroll"
>
<Column field="ticket_title" header="Ticket Title" style={{ minWidth: "200px" }}></Column>
<Column field="description" header="Description" style={{ minWidth: "350px" }}></Column>
<Column field="status" header="Status" style={{ minWidth: "200" }}></Column>
<Column field="createdAt" header="Date" style={{ minWidth: "200px" }}></Column>
</DataTable>
</div>
</div>
</div>
</div>
<div className="grid table-demo">
<div className="col-12">
<div className="card">
<h5>Ticket Info</h5>
<div>
<DataTable
// value={projects}
// sortMode="single" sortField="representative.name"
// sortOrder={1}
// scrollable
// scrollHeight="400px"
// responsiveLayout="scroll"
>
{projects.map((project, index) => (
<tr key={project.id}>
<td>{index + 1}</td>
<td>{project.ticket_title}</td>
<td>{project.ticket_description}</td>
{/* <td>{ticket.time_takes}</td> */}
<td>{project.type_menu}</td>
<td>{project.priority_menu}</td>
<td>{project.status_menu}</td>
</tr>
))}
</DataTable>
</div>
</div>
</div>
</div>
</>
);
};
export default React.memo(Projectz);
This is because the Dialog renders renderFooter outside of your <form>. I ran into the same issue.
You can fix it instead of using the footer just rendering your button to look like its in the footer.
<Dialog className="dialogModal" header="Create Ticket" visible={displayResponsive} onHide={() => onHide("displayResponsive")} breakpoints={{ "960px": "75vw" }} style={{ width: "30vw" }}>
<form onSubmit={saveProject}>
....
<div className="p-dialog-footer pb-0">
<Button label="Submit" type="submit" className="p-button-rounded p-button-success mr-2 mb-2" />
</div>
</form>
</Dialog>

I am getting stack trace error and can't resolve it

I am getting the following error and i cant figure out what do to.
Uncaught Error: The error you provided does not contain a stack trace.
at L (index.js:1)
at Y (index.js:1)
at index.js:1
at index.js:1
at o (index.js:1)
import React, { useState, useEffect } from "react";
import "./App.css";
import Post from "./Post";
import { db, auth } from "./firebase";
import { Button, Modal, Input } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
function getModalStyle() {
const top = 50;
const left = 50;
return {
top: `${top}%`,
left: `${left}%`,
transform: `translate(-${top}%, -${left}%)`,
};
}
const useStyles = makeStyles((theme) => ({
paper: {
position: "absolute",
width: 400,
backgroundColor: theme.palette.background.paper,
border: "2px solid #000",
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
function App() {
const classes = useStyles();
const [modalStyle] = React.useState(getModalStyle);
const [posts, setPosts] = useState([]);
const [open, setOpen] = useState(false);
const [username, setUsername] = useState();
const [password, setPassword] = useState();
const [email, setEmail] = useState();
useEffect(() => {
db.collection("posts").onSnapshot((snapshot) => {
setPosts(snapshot.docs.map((doc) => ({ id: doc.id, post: doc.data() })));
});
}, []);
const signUp = (e) => {
e.preventDefault();
auth
.createUserWithEmailAndPassword(email, password)
.catch((error) => alert(error.message));
};
return (
// header
<div className="app">
<Modal open={open} onClose={() => setOpen(false)}>
<div style={modalStyle} className={classes.paper}>
<form action="" className="app-signup">
<center>
<img
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="instagram"
className="app-headerImage"
/>
</center>
<Input
type="text"
placeholder="userName"
value={username}
onChange={(e) =>
setUsername(e.target.value)}
/>
<Input
type="text"
placeholder="email"
value={email}
onChange={(e) =>
setEmail(e.target.value)}
/>
<Input
type="password"
placeholder="password"
value={password}
onChange={(e) =>
setPassword(e.target.value)}
/>
<Button onClick=
{signUp}>signUp</Button>
</form>
</div>
</Modal>
<div className="app-header">
<img
src="https://www.instagram.com/static/images/web/mobile_nav_type_logo.png/735145cfe0a4.png"
alt="instagram"
className="app-headerImage"
/>
</div>
<Button onClick={() =>
setOpen(true)}>Signup</Button>
{posts.map(({ post, id }) => {
const { userName, caption, imageUrl } = post;
return (
<Post
key={id}
userName={userName}
caption={caption}
imageUrl={imageUrl}
/>
);
})}
</div>
//posts
//posts
);
}
export default App;

Can't send uploaded image url from firebase into another components in react hooks

I can't send uploaded image url from firebase storage into another component using react hooks , can anybody help me? I'm new to the react and working on this part of code about 2 weeks and did'nt have any progress, it really annoying me.
This is main component where images url should come in order to render it
import React, { useState, useEffect} from "react";
import firebase from "firebase";
import Uploader from "./uploader";
function EditorBlog() {
const [title, setTitle] = useState("");
const [text, setText] = useState("");
const [category, setCategory] = useState("");
const [url , setUrl]= useState("");
const [items, setItems] = useState([]);
const onSubmit = (data) => {
data.preventDefault();
setItems([...items, {title, text, category , url} ]);
firebase
.firestore()
.collection('blogContent')
.add({
title,
category,
text,
url
// file
})
.then(()=>{
setTitle('')
setCategory('')
setText('')
setUrl('')
});
};
return (
<div>
<form onSubmit={onSubmit} className="col-md-6 mx-auto mt-5">
<div className="form-group">
<label htmlFor="Title">Blog Title</label>
<input
type="text"
name="title"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="form-control"
id="Title"
placeholder="Arnold Schwarzneiger"
/>
</div>
<Uploader/>
<div className="form-group">
<label htmlFor="categorySelect">Select Category</label>
<select className="form-control" value={category} onChange={e => setCategory(e.target.value)} name="category" id="categorySelect">
<option>Nutrition</option>
<option>Fitness</option>
<option>Recipes</option>
<option>Succesfull Stories</option>
<option>Other</option>
</select>
</div>
<div className="form-group">
<label htmlFor="blogText">Blog Text</label>
<textarea
className="form-control"
name="text"
id="blogText"
rows="3"
value={text}
onChange={(e) => setText(e.target.value)}
></textarea>
</div>
<button
type="submit"
className="btn btn-primary offset-5"
onClick={onSubmit}
>
Save
</button>
</form>
<div className="mt-5">
{
items.map((item, index) => (
<div className="row bg-dark mx-auto ">
<div key={item.id} className="col-md-6 blogs_content mx-auto mt-5 " >
<div className="mblog_imgtop "><p>Beginner</p></div>
<img className="img-fluid editor_img " src={item.url} alt=""/>
<div className="col-md-12 rblog_txt text-center mx-auto">
<h6 className="mblog_category mx-auto m-4">{item.category}</h6>
<h2 className="blogs_title col-md-10 mx-auto">{item.title}</h2>
<div className="mt-5 mblog_innertxt col-md-11 mx-auto">{item.text}</div>
<div className="readm mt-5"><i>READ MORE</i></div>
</div>
</div>
</div>
))
}
</div>
</div>
);
}
export default EditorBlog;
this is my second component in which uploaded image url must be send into main component
import React, {useState} from "react";
import {storage} from "../../firebaseConfig";
import firebase from "firebase";
export default function Uploader() {
const [image , setImage]= useState(null);
const [url , setURL]= useState("");
const [imgURL, setImgURL] = useState('');
const [progress , setProgress]= useState(0);
const [error , setError]= useState("");
const handleChange =e =>{
const file = e.target.files[0];
if (file){
const fileType= file["type"];
const validFileType= ["image/gif", "image/png", "image/jpg", "image/jpeg"];
if (validFileType.includes(fileType)){
setError("");
setImage(file);
}else setError("Siz rasm kiritmadingiz");
}else {
setError("Iltimos faylni tanlang");
}
};
const handleUpload = props=>{
if (image){
const uploadTask =storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
"state_changed",
snapshot =>{
const progress = Math.round(
(snapshot.bytesTransferred/snapshot.totalBytes)*100
);
setProgress(progress);
},
error =>{
setError(error);
},
()=>{
storage.ref("images").child(image.name).getDownloadURL().then(url=>{
setURL(url);
console.log(url);
firebase
.firestore()
.collection('images')
.add({
url
})
.then(()=>{
setURL('')
});
setProgress(0);
});
}
)
}else setError("Xatolik Iltimos rasmni yuklang")
};
return(
<div>
<form >
<div className="form-group" >
<input type="file" className="form-control" onChange={handleChange} />
<button type="button" className="btn btn-primary btn-block" onClick={handleUpload}>Upload</button>
</div>
</form >
<div>
{progress > 0 ? <progress style={{marginLeft: "15px"}} value={progress} max="100"/> :""}
</div>
<div style={{height : "100px", marginLeft: "15px", fontWeight: "600"}}>
<p style={{color: "red"}}>{error}</p>
</div>
<img src={url || 'http://via.placeholder.com/400x300'} alt="Uploaded images" height="300" width="650"/>
</div>
)
}
Using a callback, your EditorBlog component can tell the Uploader component what to do when an upload is complete.
In other words, EditorBlog passes a function called "onComplete" as a prop to Uploader. When the upload is completed, Uploader calls this function, with the URL as an argument.
EditorBlog
export default function EditorBlog() {
const [url , setUrl] = useState(null);
// ...
const handleCompletedUpload = (url) => {
setUrl(url);
}
return (
// ...
<div>
<Uploader onComplete={handleCompletedUpload} />
<img src={url || 'http://via.placeholder.com/400x300'} alt="Uploaded images" height="300" width="650" />
</div>
// ...
);
}
Uploader:
export default function Uploader({ onComplete }) {
// ...
uploadTask.on(
"state_changed", (snapshot) => {
// ... progress update
},
(error) => {
// ... error
},
() => {
// Success. Get the URL and call the prop onComplete
uploadTask.snapshot.ref.getDownloadURL().then(url => {
onComplete(url); // Call the callback provided as a prop
});
})
// ...
}
By the way: Notice how you can get the file reference from the uploadTask directly: uploadTask.snapshot.ref.getDownloadURL(). This way you don't have to create a new reference manually, like storage.ref("images").child(image.name).getDownloadURL().

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