onSubmit React js problems - javascript

I have a problem with React JS.
I have a form where the user has to pick some options and then write a number, when the user press a button named "Consultar" or press "Enter" (Intro) it suposed to send the form and show a table with the necessary information.
However, when the user press "Enter" the form information is not submitted, the only way is by pressing the button named "Consultar". I have tried using onKeyPress but it did not work.
Could it be some react bug causing the problem and not my code? If it is not something in my code, what I need is that the user can press "Enter" and that it does the same function of the "Consultar" button.
Please help, I am very new in this world of React JS. Here is the code:
const DataForm = ({
update,
setIsLoaded,
setError,
}) => {
const formatChars = { C: '[1-9]', L: '[0-9]', Z: '[0-9]?' };
const [tipoCedula, setTipoCedula] = useState('1');
const [tipoBusqueda, setTipoBusqueda] = useState('1');
const [numCedula, setNumCedula] = useState('');
const [emailAddress, setEmailAddress] = useState('');
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
tipoCedula,
tipoBusqueda,
numCedula,
emailAddress,
});
const getNumCedulaMask = (pTipoCedula) => {
if (pTipoCedula === '2') return 'C-LLL-LLLLLL';
if (pTipoCedula === '3') return 'CLLLLLLLLLLZ';
if (pTipoCedula === '4') return 'CLLLLLLLLL';
return 'C-LLLL-LLLL';
};
const getRegExFromMask = (pMask, pFormatChars) => {
let returnValue = pMask;
Object.keys(pFormatChars).map((item) => {
if (typeof returnValue.replaceAll === 'function') {
returnValue = returnValue.replaceAll(item, pFormatChars[item]);
}
return returnValue;
});
return new RegExp(`^${returnValue}$`);
};
const numCedulaMask = getNumCedulaMask(tipoCedula);
const numCedulaRegEx = getRegExFromMask(numCedulaMask, formatChars);
const emailRegEx = '^[_a-z0-9-]+(.[_a-z0-9-]+)*#[a-z0-9-]+(.[a-z0-9-]+)*(.[a-z]{2,4})$';
const isValidNumCedula = (inputValue) => {
const replace = inputValue.replace('_', '');
if (replace.match(numCedulaRegEx)) {
return true;
}
return 'El formato no es válido';
};
const isValidEmail = (inputValue) => {
if (inputValue.match(emailRegEx)) {
return true;
}
return 'El formato no es válido';
};
const onSubmit = async (data) => {
let searchParameter = '';
if (data.tipoBusqueda === '1') {
searchParameter = data.numCedula;
} else {
searchParameter = data.emailAddress;
}
const { ssoBaseUrl } = some restricted information
const apiCall = some restricted information
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
};
setIsLoaded(true);
try {
const result = await fetch(apiCall, requestOptions);
const datos = await result.json();
setIsLoaded(false);
update('reminderResult', datos);
} catch (e) {
setError(true);
}
};
return (
<div>
<form onSubmit={handleSubmit(onSubmit)} id='formulario' data-testid='formulario-busqueda'>
<div className='form-group group_TIPO_BUSQUEDA'>
<label className='label'>Buscar por</label>
<div>
<select data-testid="search-type"
className='form-control select TIPO_BUSQUEDA'
{...register('tipoBusqueda')}
defaultValue={tipoBusqueda}
onChange={(e) => (setTipoBusqueda(e.target.value))}
>
<option value='1'>Tipo de cedula</option>
<option value='2'>Email</option>
</select></div>
</div>
{tipoBusqueda === '1' && <div className='form-group group_TIPO_CEDULA'>
<label htmlFor='tipoCedula' className='label'>Tipo de cédula</label>
<div>
<select data-testid="cedula-tipo-id"
className='form-control select TIPO_CEDULA'
{...register('tipoCedula')}
defaultValue={tipoCedula}
onChange={(e) => (setTipoCedula(e.target.value))}
>
<option value='1'>Cédula Persona Física</option>
<option value='2'>Cédula Persona Jurídica</option>
<option value='4'>Número Identificación Tributario Especial (NITE)</option>
<option value='3'>Documento Identificación Migratorio Extranjeros</option>
</select>
{errors.tipoCedula && <p className='error-message'>{errors.tipoCedula.message}</p>}
</div>
</div>
}
{tipoBusqueda === '1' && <div className='form-group group_NUM_CEDULA'>
<label htmlFor='numCedula' className='label'>Cédula</label>
<div>
<InputMask data-testid="num-cedula"
className='form-control NUM_CEDULA input'
{...register('numCedula', {
required: {
value: true,
message: 'Este campo es requerido',
},
validate: {
validValue: isValidNumCedula,
},
})}
formatChars={formatChars}
onChange={(e) => (setNumCedula(e.target.value))}
value={numCedula}
mask={numCedulaMask}
/>
{errors.numCedula && <p className='error-message' role="alert">Formato cédula incorrecto</p>}
</div>
</div>}
{tipoBusqueda === '2' && <div className='form-group'>
<label htmlFor='emailAddress' className='label'>Dirección de correo electrónico</label>
<div>
<input type="text"
data-testid="emailAddress"
className="form-control"
value={emailAddress}
{...register('emailAddress', {
required: 'Este campo es requerido',
validate: {
validValue: isValidEmail,
},
})}
onChange={(e) => (setEmailAddress(e.target.value))}
/>
{errors.emailAddress && <p className='error-message' role="alert">{errors.emailAddress.message}</p>}
</div>
</div>}
<div className='form-group'>
<button className='btn-submit float-right' type="submit" data-testid="button-ask">CONSULTAR</button>
</div>
</form>
</div>
);
};
DataForm.propTypes = {
update: PropTypes.func.isRequired,
setIsLoaded: PropTypes.func.isRequired,
setError: PropTypes.func.isRequired,
};
export default DataForm;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

I did not try your code, but i saw your code and i find here something. You have to write to arrow function in onSubmit.
<form onSubmit={() => handleSubmit(onSubmit)} id='formulario' data-testid='formulario-busqueda'>
If your page getting refresh on submit, then you have to write event.preventDefault() onSubmit.
<form onSubmit={(event) => {
event.preventDefault();
handleSubmit(onSubmit);
} id='formulario' data-testid='formulario-busqueda'>

Here is a code example how to submit forms with enter key:
import React, { useState } from 'react';
export default function App() {
const [name, setName] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
console.log('form submited');
};
return (
<div>
<form onSubmit={handleSubmit} >
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</form>
</div>
);
}
....

You don't have a handleSubmit function, what you have is an onSubmit function. Therefore, in your onSubmit event listener, you are trying to call a non-existent function
<form onSubmit={handleSubmit(onSubmit)}...
This is how you should call the onSubmit function
<form onSubmit={onSubmit}...

Related

Validating form inputs with React

I am trying to check that the required fields are not empty and making sure that the input type is correct.
const CreateSensor = () => {
const [deveui, setDeveui] = useState('');
const [location, setLocation] = useState('');
const [levelid, setLevel] = useState('');
const submitValue = () => {
let data = {deveui,location,levelid};
//POST method
fetch("api")
ClearFields();
}
function ClearFields(){
document.getElementById("dev").value = "";
document.getElementById("location").value = "";
document.getElementById("level").value = "";
}
return(
<>
<hr/>
<input type="text" id="dev" placeholder="deveui" onChange={e => setDeveui(e.target.value)} />
<input type="text" id="location"placeholder="Location" onChange={e => setLocation(e.target.value)} />
<input type="text" id="level" placeholder="Levelid" onChange={e => setLevel(e.target.value)} />
<button onClick={submitValue}>Submit</button>
</>
)
}
the submit button will check whether deveui is not empty and the levelid is set to an integer.
I have tried changing the input type for levelid to numbers but there is arrows on it which I feel is unnecessary.
I strongly recommend using a React form library. Here's an example with react-hook-form
import { useForm } from "react-hook-form";
const CreateSensor = () => {
const {
register,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm({ defaultValues: { deveui: "", location: "", levelid: "" } });
const submitValue = ({deveui, location, levelid}) => {
// exclude 'deveui' from fetch payload
const payload = { location, levelid }
// POST data to api, for example
fetch("https://myapi.com", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(payload),
// reset form state
}).then((response) => reset());
};
return (
<>
<hr />
<form onSubmit={handleSubmit(submitValue)}>
<input
{...register("deveui", { required: true })}
type="text"
id="dev"
placeholder="deveui"
/>
<input
{...register("location", { required: true })}
type="text"
id="location"
placeholder="Location"
/>
<input
{...register("levelid", { required: true })}
type="text"
id="level"
placeholder="Levelid"
/>
<button type="submit">Submit</button>
</form>
</>
);
}

Password States are reacting one character late

Im checking to see if the register forms passwords match, and when they do, something changes. but its happening on 1 "onChange" too late. Ex. User enters "DOG" as the password. when the retype it in the 2nd input, "DOG" doesnt work. but it does if they enter another character or delete one character (Ex. "DOGX" or deleting "G" so its "DO")
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
import "./register.css";
function RegisterBoard() {
const history = useHistory();
const [register, changeRegister] = useState({
password: false,
repeatPassword: false,
});
const [info, changeInfo] = useState({
password: "",
repeatPassword: "",
});
const changeValue = (e) => {
const { name, value } = e.target;
changeInfo((prev) => {
return {
...prev,
[name]: value,
};
});
};
const input = (e) => {
const target = e.target.dataset.name;
if (target != "repeatPassword") {
changeRegister({
...register,
[target]: true,
});
} else {
if (info.password != info.repeatPassword) {
changeRegister({
...register,
repeatPassword: false,
});
} else {
changeRegister({
...register,
repeatPassword: true,
});
}
}
};
return (
<div className="registration-form">
<form>
<div>
<input
name="password"
data-name="password"
onChange={(e) => {
changeValue(e);
input(e);
}}
className="password"
type="password"
placeholder="ENTER YOUR PASSWORD HERE"
/>
<div className="animated-button">
</div>
</div>
<div>
<input
id="pwd"
name="repeatPassword"
data-name="repeatPassword"
onChange={(e) => {
changeValue(e);
input(e);
}}
className="repeat-password"
type="password"
placeholder="REPEAT YOUR PASSWORD HERE"
/>
</div>
</div>
</form>
</div>
);
}
export default RegisterBoard;
I guess this is because you are calling both 'changeValue' and 'input' functions within the inputs onChange attribute. Since they are firing at the same time, 'input' is not using the most recent value for 'info', because 'changeValue' hasn't set the new state yet.
Either call the input function within a useEffect hook which is dependent on changes to 'info's' state, or use e.target.value instead of info's state within the 'input' function to compare info.password != info.repeatPassword
EDIT: here is the useEffect way, it simplifies it and you can remove your input function completely: https://codesandbox.io/s/jolly-khorana-8s63b?file=/src/App.js
import React, { useState, useEffect } from "react";
import "./styles.css";
function RegisterBoard() {
const [register, changeRegister] = useState({
password: false,
repeatPassword: false
});
const [info, changeInfo] = useState({
password: "",
repeatPassword: ""
});
const changeValue = (e) => {
const { name, value } = e.target;
changeInfo((prev) => {
return {
...prev,
[name]: value
};
});
};
useEffect(() => {
let password = false;
let repeatPassword = false;
if (info.password !== "") {
password = true;
if (info.password === info.repeatPassword) {
repeatPassword = true;
}
}
changeRegister({ password, repeatPassword });
}, [info]);
return (
<div className="registration-form">
<form>
<div>
<input
name="password"
data-name="password"
onChange={changeValue}
className="password"
type="password"
placeholder="ENTER YOUR PASSWORD HERE"
/>
<div className="animated-button"></div>
</div>
<div>
<input
id="pwd"
name="repeatPassword"
data-name="repeatPassword"
onChange={changeValue}
className="repeat-password"
type="password"
placeholder="REPEAT YOUR PASSWORD HERE"
/>
</div>
</form>
<div>{info.password}</div>
<div>{info.repeatPassword}</div>
<div>{register.repeatPassword ? "match" : "don't match"}</div>
</div>
);
}
export default function App() {
return (
<div className="App">
<RegisterBoard />
</div>
);
}
You're definitely going to want to implement a useEffect here to update the UI every time the password & repeatPassword state changes, to ensure that after the last character is typed that you get the full password. Inside the useEffect is where you'll write your conditional logic. What I provided is just a good example...
import React, { useState, useEffect } from "react";
import { useHistory } from "react-router-dom";
import "./register.css";
function RegisterBoard() {
const history = useHistory();
const [password, setPassword] = useState('')
const [repeatPassword, setRepeatPassword] = useState('')
//const [register, changeRegister] = useState(false);
const changeValue = (e) => {
const { name, value } = e.target.value;
const input = (e) => {
const target = e.target.dataset.name;
if (target != "repeatPassword") {
changeRegister({
...register,
[target]: true,
});
} else {
if (info.password != info.repeatPassword) {
changeRegister({
...register,
repeatPassword: false,
});
} else {
changeRegister({
...register,
repeatPassword: true,
});
}
}
};
useEffect(() => {
if((password !== "" && repeatPassword !== "") && (password !==
repeatPassword)){
console.log("PASSWORDS DO NOT MATCH!!!")
}
console.log(password, repeatPassword)
}, [password, repeatPassword])
return (
<div className="registration-form">
<form>
<div>
<input
name="password"
data-name="password"
onChange={(e) => changeValue(e)}
className="password"
type="password"
placeholder="ENTER YOUR PASSWORD HERE"
/>
<div className="animated-button">
</div>
</div>
<div>
<input
id="pwd"
name="repeatPassword"
data-name="repeatPassword"
onChange={(e) => changeValue(e)}
className="repeat-password"
type="password"
placeholder="REPEAT YOUR PASSWORD HERE"
/>
</div>
</div>
</form>
</div>
);
}
export default RegisterBoard;

Why can't I type anything into my form inputs

I have a ReactJS app. I am trying to implement form validation by the accepted answer here as my example. I had to adapt it some because I am using a Function and not a Component (i.e. I don't have a constructor).
Here is the whole function:
import React, { useState } from "react";
import Config from 'config';
import PropTypes from "prop-types";
import "./Login.css";
export default function ChangePassword({ setToken }) {
const [fields, setFields] = useState({});
const [errors, setErrors] = useState({});
const tokenString = sessionStorage.getItem("token");
const token = JSON.parse(tokenString);
let headers = new Headers({
"Accept": "application/json",
"Content-Type": "application/json",
'Authorization': 'Bearer ' + token.token
});
async function changePassword(password) {
return fetch(Config.apiUrl + "/api/Users/ChangePassword", {
method: "POST",
headers: headers,
body: password
})
.then((response) => {
return response.json();
});
}
function handleValidation() {
let formErrors = {};
let formIsValid = true;
//Password
if (!fields["password"]) {
formIsValid = false;
formErrors["password"] = "Cannot be empty";
}
if (typeof fields["password"] !== "undefined") {
if (!fields["password"].match(/^[a-zA-Z0-9!##$%^&?]+$/)) {
formIsValid = false;
formErrors["password"] = "Only accepted";
}
}
//Confirm Password
if (!fields["confirmPassword"]) {
formIsValid = false;
formErrors["confirmPassword"] = "Cannot be empty";
}
if (typeof fields["confirmPassword"] !== "undefined") {
if (!fields["confirmPassword"].match(/^[a-zA-Z0-9!##$%^&?]+$/)) {
formIsValid = false;
formErrors["confirmPassword"] = "Only accepted";
}
}
if (fields["confirmPassword"] != fields["password"]) {
formIsValid = false;
formErrors["confirmPassword"] = "Does not match Password";
}
setErrors({ formErrors });
return formIsValid;
}
const handleSubmit = async e => {
e.preventDefault();
if (handleValidation()) {
const myToken = await changePassword(fields["password"]);
setToken(myToken);
} else {
alert("Form has errors.")
}
}
function handleChange(field, e) {
let myFields = fields;
myFields[field] = e.target.value;
setFields({ myFields });
}
return (
<div className="login wrapper fadeInDown">
<div className="login formContent ">
<h1 className="login centered">Change Password</h1>
<form onSubmit={handleSubmit}>
<div className="centered">
<span style={{ color: "red" }}>{errors["password"]}</span>
<input type="password" value={fields["password"] || ""} onChange={e => handleChange.bind(this, "password")} id="login" className="login fadeIn second" placeholder="Password" />
</div>
<div className="centered">
<span style={{ color: "red" }}>{errors["confirmPassword"]}</span>
<input type="password" value={fields["confirmPassword"] || ""} onChange={e => handleChange.bind(this, "confirmPassword")} id="login" className="login fadeIn third" placeholder="Confirm Password" />
</div>
<div className="centered">
<input type="submit" className="login fadeIn fourth" value="Submit" />
</div>
</form>
</div>
</div>
)
}
ChangePassword.propTypes = {
setToken: PropTypes.func.isRequired
}
The function loads, but I cannot type anything in Password or Confirm Password. For the life of me I can't figure out why. I am hoping a kind SO user will point out my error(s).
UPDATE
Just to be complete... Based on the comment by #evolutionxbox I changed my function changePassword to:
async function changePassword(password) {
const response = await fetch(Config.apiUrl + "/api/Users/ChangePassword", {
method: "POST",
headers: headers,
body: JSON.stringify(password)
})
const json = await response.json();
console.log(json);
return json;
}
As other commenters mentioned, you don't need to .bind in a non-class context.
Change your onChange event handlers from
onChange={e => handleChange.bind(this, "password")}
Add a name attribute to your input elements:
<input name="password" ... />
<input name="confirmPassword" ... />
to
onChange={handleChange}
Then you can update your handleChange function to the following:
function handleChange(e) {
setFields({
...fields,
[e.target.name]: e.target.value
});
}
The above setState has the added benefit of not mutating your existing state object
function handleChange(field, e) {
let myFields = fields;
myFields[field] = e.target.value;
setFields({ myFields });
}
It is a mistake that most new people in react make. UseState is immutable and when you are saying
let myFields = fields;
You are copying a reference to your fields useState. Try destructuring, that will copy the value not reference
let myFields = [...fields];
But my advice would be to create 2 useStates one for password, another for confirmPassword, it will make your code clearer and wont mess up the states. Then change your input element in jsx from that
<input type="password" value={fields["password"] || ""} onChange={e => handleChange.bind(this, "password")} id="login" className="login fadeIn second" placeholder="Password" />
to that
<input type="password" value={fields["password"] || ""} onChange={e => setPassword(e.target.value)} id="login" className="login fadeIn second" placeholder="Password" />

After editing only one field is edited and the other one behaves unexpectedly (React hooks)

I have very simple ediable book-list. But when I press 'edit' and edit just one of two fields the other one behaves unexpectedly (it clears out even tho there was a value, or takes unexpected value from God knows where). It works correctly only if I edit both fields at once.
Here's my code:
import './App.css';
import React, { useState, useEffect } from 'react';
function App() {
const [books, setBooks] = useState([]);
const [book, setBook] = useState({
id: '',
title: '',
author: ''
});
const [alertMessage, setAlertMessage] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [editMode, setEditMode] = useState(null);
const [titleValue, setTitleValue] = useState('');
const [authorValue, setAuthorValue] = useState('');
useEffect(()=> {
const data = localStorage.getItem('books');
if(data) {
setBooks(JSON.parse(data))
}
}, [])
useEffect(()=> {
localStorage.setItem('books', JSON.stringify(books))
},)
function addBook (e) {
e.preventDefault();
if(!book.title && !book.author){
setAlertMessage(true)
setTimeout(()=>setAlertMessage(false), 3000)
} else {
let newBook = {
...book,
id: Math.floor(Math.random() * 100000000),
};
setBooks([newBook, ...books]);
setBook({
title: '',
author: ''
});
setSuccessMessage(true)
setTimeout(()=>setSuccessMessage(false), 1000);
}
}
function deleteBook(id){
setBooks(books.filter(book => book.id !== id))
}
function editBook(id) {
setEditMode(id);
}
function onChange(e) {
setBook({
...book,
[e.target.name]: e.target.value
})
}
function saveChanges (id) {
let newBook = [...books].map(book => {
if(book.id === id) {
book.title = titleValue;
book.author = authorValue
}
return book
});
setBook(newBook);
setEditMode(null)
}
return (
<div className='container'>
{alertMessage && <div className='alertMeaage'>Please, enter book author or its title</div>}
{successMessage && <div className='successMessage'>Book is successfully added!</div>}
<div className='BookForm'>
<h3>Add book</h3>
<input name='title' type='text' placeholder='Enter book title' value={book.title} onChange={onChange}/>
<input name='author' type='text' placeholder='Enter book author' value={book.author} onChange={onChange}/>
<button className='submitBtn' onClick={addBook}>Send</button>
</div>
<div>
<h4>Recently added books:</h4>
<div key={book.id}>{books.map(book => (
<div className='bookItem'>
{editMode !== book.id ? <><span className='titleAuthor'>Title: </span><i>«{book.title}» </i>
<span className='titleAuthor'>Author: </span> <i>{book.author}</i>
<button onClick={()=>deleteBook(book.id)} className='deleteBtn'>X</button>
<button onClick={()=>editBook(book.id)} className='editBtn'>Edit</button></>
:
<form className='form'>
<input name='title' type='text' defaultValue={book.title} onChange={(e)=> setTitleValue(e.target.value)}/>
<input name='author' type='text' defaultValue={book.author} onChange={(e)=> setAuthorValue(e.target.value)}/>
<button className='saveBtn' onClick={()=>saveChanges(book.id)}>Save</button>
</form>
}
</div>
))}
</div>
</div>
</div>
);
}
export default App;
Thanks a lot in advance!
When you edit new book, authorValue and titleValue still have previous values, so you must setAuthorValue and setTitleValue in editBook function. See below:
function editBook(book) {
setEditMode(book.id);
setTitleValue(book.title);
setAuthorValue(book.author);
}
And handle event:
<button onClick={() => editBook(book)} className="editBtn">
Edit
</button>
All code:
// import './App.css';
import React, { useState, useEffect } from "react";
function App() {
const [books, setBooks] = useState([]);
const [book, setBook] = useState({
id: "",
title: "",
author: ""
});
const [alertMessage, setAlertMessage] = useState(false);
const [successMessage, setSuccessMessage] = useState(false);
const [editMode, setEditMode] = useState(null);
const [titleValue, setTitleValue] = useState("");
const [authorValue, setAuthorValue] = useState("");
useEffect(() => {
const data = localStorage.getItem("books");
if (data) {
setBooks(JSON.parse(data));
}
}, []);
useEffect(() => {
localStorage.setItem("books", JSON.stringify(books));
});
function addBook(e) {
e.preventDefault();
if (!book.title && !book.author) {
setAlertMessage(true);
setTimeout(() => setAlertMessage(false), 3000);
} else {
let newBook = {
...book,
id: Math.floor(Math.random() * 100000000)
};
setBooks([newBook, ...books]);
setBook({
title: "",
author: ""
});
setSuccessMessage(true);
setTimeout(() => setSuccessMessage(false), 1000);
}
}
function deleteBook(id) {
setBooks(books.filter((book) => book.id !== id));
}
function editBook(book) {
setEditMode(book.id);
setTitleValue(book.title);
setAuthorValue(book.author);
}
function onChange(e) {
console.log(e.target.name, e.target.value);
setBook({
...book,
[e.target.name]: e.target.value
});
}
function saveChanges(id) {
let newBook = [...books].map((book) => {
if (book.id === id) {
book.title = titleValue;
book.author = authorValue;
}
return book;
});
setBook(newBook);
setEditMode(null);
}
return (
<div className="container">
{alertMessage && (
<div className="alertMeaage">
Please, enter book author or its title
</div>
)}
{successMessage && (
<div className="successMessage">Book is successfully added!</div>
)}
<div className="BookForm">
<h3>Add book</h3>
<input
name="title"
type="text"
placeholder="Enter book title"
value={book.title}
onChange={onChange}
/>
<input
name="author"
type="text"
placeholder="Enter book author"
value={book.author}
onChange={onChange}
/>
<button className="submitBtn" onClick={addBook}>
Send
</button>
</div>
<div>
<h4>Recently added books:</h4>
<div key={book.id}>
{books.map((book) => (
<div className="bookItem">
{editMode !== book.id ? (
<>
<span className="titleAuthor">Title: </span>
<i>«{book.title}» </i>
<span className="titleAuthor">Author: </span>{" "}
<i>{book.author}</i>
<button
onClick={() => deleteBook(book.id)}
className="deleteBtn"
>
X
</button>
<button onClick={() => editBook(book)} className="editBtn">
Edit
</button>
</>
) : (
<form className="form">
<input
name="title"
type="text"
defaultValue={book.title}
onChange={(e) => setTitleValue(e.target.value)}
/>
<input
name="author"
type="text"
defaultValue={book.author}
onChange={(e) => setAuthorValue(e.target.value)}
/>
<button
className="saveBtn"
onClick={() => saveChanges(book.id)}
>
Save
</button>
</form>
)}
</div>
))}
</div>
</div>
</div>
);
}
export default App;

React Required Input

I'm trying to require the name and email in my form. I originally had <button> nested within a <Link> and figured out that the <button> worked by itself to allow the input to be required, but that when it's nested within the <Link> it doesn't require it. I then added the link to be within the submitSurvey function and took the <Link> away from the render. It still won't require the input.
Here is my NameEmailComponent.js:
import React, { useState } from "react";
import { NameInput } from "../inputs/NameInput";
import { EmailInput } from "../inputs/EmailInput";
import { useHistory } from "react-router-dom";
export const NameEmailComponent = (props) => {
const [surveyValues, setSurveyValues] = useState({});
const [inlineData, setInlineData] = useState({});
const [question, setQuestion] = useState({});
const history = useHistory();
console.log(props);
const triggerBackendUpdate = () => {
setSurveyValues({});
setQuestion({});
};
const handleSubmit = (event) => {
event.preventDefault();
event.persist();
setSurveyValues(surveyValues);
setQuestion(question);
triggerBackendUpdate();
};
const callback = (name, value) => {
console.log("Form Data: ", name, ": ", value);
inlineData[name] = value;
setInlineData(inlineData);
console.log(inlineData);
};
const handleChange = (event) => {
event.preventDefault();
this.setState({ value: event.target.value });
console.log("Name: ", event.target.value);
};
const submitSurvey = async () => {
try {
await fetch("/api/survey", {
method: "POST",
body: JSON.stringify(inlineData),
headers: {
"Content-Type": "application/json",
},
});
history.push({ pathname: "/survey" });
} catch (err) {
console.log(err);
}
};
const inputs = props.inputs
? props.inputs.filter((inputOption) => inputOption)
: [];
return (
<>
<div id="nameContainer" className="form-group">
<form onSubmit={handleSubmit}>
{inputs.map((data, index) => {
let inputKey = `input-${index}`;
return data.type === "text" ? (
<NameInput
className="form-control my-3"
triggerCallback={callback}
name={data.name}
type={data.type}
placeholder={data.placeholder}
required={true}
onChange={handleChange}
key={inputKey}
/>
) : (
<EmailInput
className="form-control mt-3"
triggerCallback={callback}
name={data.name}
type={data.type}
placeholder={data.placeholder}
required={true}
onChange={handleChange}
key={inputKey}
/>
);
})}
<div className="col-6 mx-auto text-center">
<div className="button">
<button
className="btn btn-primary mt-4 mb-2 mx-5"
type="submit"
onClick={submitSurvey}
>
Begin Survey
</button>
</div>
</div>
</form>
</div>
</>
);
};
Here is my NameInput.js:
import React from "react";
import { useInputChange } from "../Hooks/useInputChangeHook";
import { isTextInput } from "../validators";
export const NameInput = (props) => {
const inputType = isTextInput(props.type) ? props.type : "text";
const { handleChange } = useInputChange(
props.defaultValue,
props.triggerCallback,
inputType
);
const inputProps = {
className: props.className ? props.className : "form-control",
onChange: handleChange,
required: props.required,
question: props.question,
placeholder: props.placeholder,
type: inputType,
options: props.options,
name: props.name ? props.name : `${inputType}_${props.key}`,
};
return (
<>
<div id={props.name}>
<input
{...inputProps}
required={props.required}
value={props.value || ""}
/>
</div>
</>
);
};

Categories