I'm creating a register form in react with validation. The values i'm asking is Username, Email, password + (controll password). Everything about the form works, validations, Errors and if you click sign up you go to a new page. Now i want to extract the values to a my MySql database. I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form.
I have tried
onChange={(e) => {
setUsernameReg(e.target.value);
}}
(see commented item)
But when i tried this I couldn't fill anything in Username. The code for the other inputs (email, password) is the same apart from the names.
So in short I want to get the value what you typed in a textbox to my database.
Code: FormSignup.js
import React, { useEffect, useState } from 'react';
import Axios from 'axios';
import validate from './validateInfo';
import useForm from './useForm';
import './Form.css';
const FormSignup = ({ submitForm }) => {
const { handleChange, handleSubmit, values, errors } = useForm(
submitForm,
validate
);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
return (
<div className='form-content-right'>
<form onSubmit={handleSubmit} className='form' noValidate>
<h1>
Get started with us today! Create your account by filling out the
information below.
</h1>
<div className='form-inputs'>
<label className='form-label'>Username</label>
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Code UseForm.js
import { useState, useEffect } from 'react';
import Axios from 'axios';
const useForm = (callback, validate) => {
const [values, setValues] = useState({
username: '',
email: '',
password: '',
password2: ''
});
const [errors, setErrors] = useState({});
const [isSubmitting, setIsSubmitting] = useState(false);
const [usernameReg, setUsernameReg] = useState("");
const [emailReg, setEmailReg] = useState("");
const [passwordReg, setPasswordReg] = useState("");
Axios.defaults.withCredentials = true;
const register = () => {
Axios.post("http://localhost:3001/register", {
username: usernameReg,
password: passwordReg,
email: emailReg,
}).then((response) => {
console.log(response);
});
};
const handleChange = e => {
const { name, value } = e.target;
setValues({
...values,
[name]: value
});
};
const handleSubmit = e => {
e.preventDefault();
setErrors(validate(values));
setIsSubmitting(true);
};
useEffect(
() => {
if (Object.keys(errors).length === 0 && isSubmitting) {
callback();
}
},
[errors]
);
return { handleChange, handleSubmit, values, errors };
};
export default useForm;
The code is from https://www.youtube.com/watch?v=KGFG-yQD7Dw&t and https://www.youtube.com/watch?v=W-sZo6Gtx_E&t
By using the value prop of the input, you turn it into a controled input element and thus need to update its value via a state variable. So this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={values.username}
onChange={handleChange}
/*
//onChange={(e) => {
// setUsernameReg(e.target.value);
//}}
*/
/>
Should just be this:
<input
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
value={usernameReg}
onChange={e => setUsernameReg(e.target.value)}
/>
Note that this only answers this part:
I succeed in putting stuff in my database so the link works but i can't get the values of what i typed in the form
So this is how you can access those values. I can't guide you on how to get those values all the way to your DB as there is a longer distance they have to travel and I don't know what else could be in the way.
You should also look into useRef(), which will give you access to those input fields without updating your state on every change of the input and thus re-rendering your form over and over.
You can do something like this:
...
const regInput = React.useRef();
...
...
<input
ref={regInput}
className='form-input'
type='text'
name='username'
placeholder='Enter your username'
Then when you're ready to submit, just access the value of the username input like so:
...
const v = regInput.current.value;
...
Related
I am trying to update the context through the "state" variable here a then render it in another component. The rendering works fine, but not the first time of submitting the form here, only the second time, third time etc. Any idea how to solve this? I tried to move dispatchDataState({ type: "ADD_DATA", payload: state }) into useEffect but then I cannot remove the entered value in the inputs after submitting it. Any idea?
import React, { useContext, useState, useEffect } from "react";
import DataContext from "../store/data-context";
function Form() {
const [name, setName] = useState("");
const [secName, setSecName] = useState("");
const [tel, setTel] = useState("");
const [note, setNote] = useState("");
const [state, setState] = useState({
name: "",
secName: "",
tel: "",
note: "",
});
const { dispatchDataState } = useContext(DataContext);
const handleSubmit = (e) => {
e.preventDefault();
console.log(name);
setState({
name: name,
secName: secName,
tel: tel,
note: note,
});
dispatchDataState({ type: "ADD_DATA", payload: state });
console.log(state);
};
return (
<div>
<form onSubmit={handleSubmit}>
<label>
Jméno
<input
type="text"
required
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<label>
Příjmení
<input
type="text"
required
value={secName}
onChange={(e) => setSecName(e.target.value)}
/>
</label>
<label>
Telefonní číslo
<input
type="text"
required
value={tel}
onChange={(e) => setTel(e.target.value)}
/>
</label>
<label>
Poznámka
<input
type="text"
value={note}
onChange={(e) => setNote(e.target.value)}
/>
</label>
<input type="submit" value="Odeslat" />
</form>
</div>
);
}
export default Form;
Part of the problem is: useState set method not reflecting change immediately And you are highly encouraged to read and understand that.
But the root of the problem here seems to be that you are also duplicating state. Storing the same values in multiple state variables and trying to keep them synchronized manually. Why?
Just keep the state values you want:
const [name, setName] = useState("");
const [secName, setSecName] = useState("");
const [tel, setTel] = useState("");
const [note, setNote] = useState("");
And then dispatch the context using those values:
const handleSubmit = (e) => {
e.preventDefault();
const state = {
name,
secName,
tel,
note,
};
dispatchDataState({ type: "ADD_DATA", payload: state });
console.log(state);
};
Or, if you want the "state" to be an object:
const [state, setState] = useState({
name: "",
secName: "",
tel: "",
note: ""
});
And dispatch that:
const handleSubmit = (e) => {
e.preventDefault();
dispatchDataState({ type: "ADD_DATA", payload: state });
console.log(state);
};
Of course in that case you'd also have to update your change handlers. For example:
<input
type="text"
required
value={state.name}
onChange={(e) => setState({ ... state, name: e.target.value })}
/>
And so on for your other input fields.
But overall the point is that "state" doesn't replace all possible variables in React. It's just there for, well, storing state. You can create new variables like anywhere else in JavaScript in order to re-structure the data from state for other purposes.
I'm kinda new in programming and I'm learning how the web works, And Im working on this project using Supabase PostgreeSQL with Api, so what I want to do is write (send data to db), and I want to do it using only ReactJS but I don't know if this is possible, I have made something to work only with JS and the API keyes from SUPABASE, but I don't know if this is correct to do
import {useState} from 'react'
import {supabase} from '../db/supabase';
import Helmet from "helmet";
import {Link} from 'react-router-dom'
import styled from "styled-components";
const Container = styled.div``
const LeftContainer = styled.div``
const RightContainer = styled.div``
const Input = styled.input``
const BtnButBlack = styled.button``
const BtnButGreen = styled.button``
const Form = styled.form``
export default function Signup() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [name, setName] = useState('');
const [username, setUsername] = useState('');
const handleSubmit = (event) => {
new_account({email: email, password: password}, name, username)
event.preventDefault();
};
const handleEmail = (event) => {
event.preventDefault();
setEmail(event.target.value)
};
const handlePassword = (event) => {
event.preventDefault();
setPassword(event.target.value)
}
const handleName = (event) => {
event.preventDefault();
setName(event.target.value)
}
const handleUsername = (event) => {
event.preventDefault();
setUsername(event.target.value)
}
const new_account = async ({email, password}) => {
const {user, session, error} = await supabase.auth.signUp({
email: email,
password: password,
})
if (error) {
console.error(error)
return
}
console.log(user);
await supabase
.from('Users')
.insert([
{username: username, name: name, email: email, password: password}
])
};
return (
<Container>
<LeftContainer>
Signup
</LeftContainer>
<RightContainer>
<Form onSubmit={handleSubmit}>
<Input onChange={handleUsername} type="text" value={username} placeholder="Username" autoComplete="false" /><br></br>
<Input onChange={handleName} type="text" value={name} placeholder="Name" autoComplete="false" /><br></br>
<Input onChange={handleEmail} type="text" value={email} placeholder="Email" autoComplete="false" /><br></br>
<Input onChange={handlePassword} type="password" value={password} placeholder="Password" autoComplete="false" /><br />
<BtnButGreen>Sign Up</BtnButGreen>
</Form>
</RightContainer>
</Container>
)
}
and this is the supabase module that you can import and use it on the jsx file:
import {createClient} from '#supabase/supabase-js'
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey)
The code you have seems great! With Supabase, the you are only exposing anon key and supabase URL, which are meant to be public.
You use row level security and Supabase Auth to prevent malicious attackers to hack your database. You can read more about row level security here:
https://supabase.io/docs/learn/auth-deep-dive/auth-row-level-security
If you have any questions, please don't hesitate to ask!
I do have a simple component with form. I want to use useSendEmail hook which returns the server response (success or failure). Where do I call this hook so it doesn't fire on the first render but only after I get my data from the user and save it in the state?
Expected behaviour: hook useSendEmail is called when the email object contains users input and returns status (if sent successfully or not).
I am aware I need to call hook at the top level in the component, but I do I wait for the data from input fields?
Actual behaviour: I am breaking the rule of hooks.
// importing everything I need here
const ContactPage = () => {
const initial = {
from: '',
message: '',
email: '',
};
const [formData, setFormData] = useState(initial);
const [email, setEmail] = useState(null);
const handleChange = ({ name, value }) => {
setFormData({ ...formData, [name]: value });
};
useEffect(() => {
if (email === null) return;
const response = useSendEmail(email);
}, [email]);
const handleSubmit = (e) => {
e.preventDefault();
setEmail(formData);
};
return (
<DefaultLayout title="Contact">
<StyledContainer>
<form className="contact_form" onSubmit={(e) => handleSubmit(e)}>
<input
name="from"
type="text"
value={formData.from}
onChange={(e) => handleChange(e.target)}
placeholder="Your full name"
/>
<textarea
name="message"
value={formData.message}
onChange={(e) => handleChange(e.target)}
placeholder="Your Message"
/>
<input
name="email"
type="email"
value={formData.email}
onChange={(e) => handleChange(e.target)}
placeholder="Your e-mail"
/>
<button type="submit">SUBMIT</button>
</form>
</StyledContainer>
</DefaultLayout>
);
};
export default ContactPage;
EDIT:
this is how my hook looks like after refactoring with your suggestions. I am now importing the hook and the method in the top level component and everything seems to work perfectly.
import { useState } from 'react';
import emailjs from 'emailjs-com';
import { userID, templateID, serviceID } from '../data/account';
const useSendEmail = (email) => {
const [response, setResponse] = useState(null);
const successMsg = 'Your message has been successfully sent';
const errorMsg = 'Your message has not been sent. Try again.';
const sendEmail = async () => emailjs
.send(serviceID, templateID, email, userID)
.then(
(res) => {
if (res.status === 200) {
setResponse(successMsg);
}
if (res.status !== 200) {
setResponse(errorMsg);
}
},
(err) => {
console.log(err);
},
);
return { response, sendEmail }
};
export default useSendEmail;
I'm btrying to save an array of objects in local storage, each time a user clicks a button, i add the username and email fron input fields
but it keeps updating the local storage instead of appending new object to the array
Below is my code
const app = () => {
const [allusers,setAllusers] = useState([JSON.parse(localStorage.getItem('users')) || '']);
const [id,setId] = useState(0);
const [newuser,setNewuser] = useState({
'id':id
'name':'David',
'email':'david#gmail.com'
})
const handleChange = () =>{
setNewuser({...newuser,[e.target.name] : e.target.value});
}
const add = ()=>{
setAllusers([newuser])
localStorage.setItem('users',JSON.stringify(allusers))
setID(id+1); // increase id by 1
}
return(
<div>
<form>
<input type="text" name="user" onChange={handleChange}>
<input type="text" name="email" onChange={handleChange}>
<button onclick={()=>save}>Save</button>
</form>
</div>
)
}
export default app;
There were a lot of syntactical errors and use of functions like save which was never declared and still used.
I rewrote the whole example and made it a bit modular so that you can comprehend it better.
Here is the working example:
Final Output:
Full Source code:
import React, { useState, useEffect } from "react";
import "./style.css";
const App = () => {
const [allusers, setAllusers] = useState([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const handleName = e => {
setName(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const save = e => {
e.preventDefault();
let newUsers = {
id: Math.floor(Math.random() * 100000),
name: name,
email: email
};
localStorage.setItem("users", JSON.stringify([...allusers, newUsers]));
setAllusers(allusers.concat(newUsers));
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
};
useEffect(() => {
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
if (localStorage.getItem("users")) {
setAllusers(JSON.parse(localStorage.getItem("users")));
}
}, []);
return (
<div>
<form>
<input type="text" name="user" onChange={handleName} />
<input type="text" name="email" onChange={handleEmail} />
<button onClick={save}>Save</button>
<p>{JSON.stringify(allusers)}</p>
</form>
</div>
);
};
export default App;
As You inquired in the comment section, here is how you can implement the Update functionality:
Final Output:
Full source code:
import React, { useState, useEffect } from "react";
import "./style.css";
const App = () => {
const [allusers, setAllusers] = useState([]);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [id, setId] = useState(null);
const handleName = e => {
setName(e.target.value);
};
const handleEmail = e => {
setEmail(e.target.value);
};
const save = e => {
e.preventDefault();
let newUsers = {
id: Math.floor(Math.random() * 100000),
name: name,
email: email
};
localStorage.setItem("users", JSON.stringify([...allusers, newUsers]));
setAllusers(allusers.concat(newUsers));
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
};
const setForUpdate = user => {
setName(user.name);
setEmail(user.email);
setId(user.id);
};
const update = e => {
e.preventDefault();
let modifiedData = allusers.map(user => {
if (user.id === id) {
return { ...user, name: name, email: email };
}
return user;
});
setAllusers(modifiedData);
localStorage.setItem("users", JSON.stringify(modifiedData));
setId(null);
};
useEffect(() => {
console.log("Localstorage:", JSON.parse(localStorage.getItem("users")));
if (localStorage.getItem("users")) {
setAllusers(JSON.parse(localStorage.getItem("users")));
}
}, []);
return (
<div>
<form>
<input value={name} type="text" name="user" onChange={handleName} />
<input value={email} type="text" name="email" onChange={handleEmail} />
<button disabled={!(id == null)} onClick={save}>
Save
</button>
<button disabled={id == null} onClick={update}>
Update
</button>
</form>
{allusers &&
allusers.map(user => (
<div className="userInfo">
<p>{user.name}</p>
<p>{user.email}</p>
<button onClick={() => setForUpdate(user)}>
select for update
</button>
</div>
))}
</div>
);
};
export default App;
You can find the working example here: Stackblitz
You are trying to save allusers to the localStorage right after setAllUsers() but setState is asynchronous. The value does not have to be updated on the next line. You can read more about it at reactjs.org, Why is setState giving me the wrong value?.
I would recommend to use useEffect.
const add=()=> {
setAllusers([... allusers ,newuser])
}
useEffect(()=>{
// this is called only if the variable `allusers` changes
// because I've specified it in second argument of useEffect
localStorage.setItem('users',JSON.stringify(allusers))
}, [allusers]);
()=>handleChange is a function that takes no arguments and returns the handleChange function. You probably want () => handleChange(), which would take no arguments and INVOKE handleChange.
you are adding only one new user while clicking on add button. You need to copy previous data also when setting all users.
Second thing setting state is async and hence your localStorage and allusers may have different value and to avoid this one you need to use useEffect to set the value.
const add = ()=>{
setAllusers([...allusers ,newuser])
setID(id+1); // increase id by 1
}
useEffect(() => {
localStorage.setItem('users',JSON.stringify(allusers))
},[allusers])
In order to improve my React skills, I have been trying to build a reusable form state hook and form validator. My custom hook, FormState, initializes an object with empty strings for values to be used as an initial state for my hook. I wrote a function, clearInputs, that I expected to reset my inputs to their initial state but it is failing to update.
I've poked around looking for an answer, even referring to this Stack Overflow post: Reset to Initial State with React Hooks . Still no dice.
// MY FORMSTATE HOOK
import { useState } from 'react';
const FormState = props => {
let initialState = { ...props };
const [ inputs, setInputs ] = useState(initialState);
const [ errors, setErrors ] = useState(initialState);
const handleInput = e =>
setInputs({
...inputs,
[e.target.name]: e.target.value
});
const handleError = errs => setErrors({ ...errors, ...errs });
const resetForm = () => {
setInputs({ ...initialState });
setErrors({ ...initialState });
};
const clearInputs = () => {
console.log('SUPPOSED TO CLEAR', initialState)
console.log('MY INPUTS', inputs)
setInputs({ ...initialState });
console.log('AFTER THE SETTING', inputs)
};
return [ inputs, handleInput, errors, handleError, resetForm, clearInputs ];
};
export default FormState;
// REGISTER FORM
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import FormState from './formState';
import Field from './field';
import { registerUser } from '../../actions/users';
import './forms.css';
const RegisterForm = props => {
const isLoggedIn = localStorage.getItem('user');
useEffect(
() => {
if (isLoggedIn) {
const parsedUser = JSON.parse(isLoggedIn);
props.history.push(`/profile/${parsedUser.pk}`);
}
},
[ isLoggedIn ]
);
const initialInputs = {
username: '',
password1: '',
password2: '',
first_name: '',
last_name: ''
};
const [ inputs, handleInput, errors, handleErrors, resetForm, clearInputs ] = FormState(initialInputs);
const handleSubmit = e => {
e.preventDefault();
const validForm = validate(inputs, handleErrors);
if (validForm) {
props.registerUser(inputs);
resetForm();
}
else {
clearInputs();
}
};
return (
<div className='form-wrap'>
<h1>Register Here</h1>
<form className='form' onSubmit={handleSubmit}>
<Field
label='Username'
fieldname='username'
value={inputs.username}
placeholder='Enter Your Username'
fielderror={errors.username}
handleInput={handleInput}
classNames='form-section'
/>
<Field
label='Password'
fieldname='password1'
value={inputs.password1}
placeholder='Enter Your Password'
fielderror={errors.password1}
handleInput={handleInput}
classNames='form-section'
/>
<Field
label='Confirm Password'
fieldname='password2'
value={inputs.password2}
placeholder='Confirm Your Password'
fielderror={errors.password2}
handleInput={handleInput}
classNames='form-section'
/>
<Field
label='First Name'
fieldname='first_name'
value={inputs.first_name}
placeholder='Enter Your First Name'
fielderror={errors.first_name}
handleInput={handleInput}
classNames='form-section'
/>
<Field
label='Last Name'
fieldname='last_name'
value={inputs.last_name}
placeholder='Enter Your Last Name'
fielderror={errors.last_name}
handleInput={handleInput}
classNames='form-section'
/>
<button type='submit' className='submit-button'>
Submit
</button>
</form>
</div>
);
};
const validate = (inputs, handleErrors) => {
let errs = {};
const { username, password1, password2, first_name, last_name } = inputs;
if (!username) {
errs.username = 'Username is missing';
}
if (!password1) {
errs.password1 = 'Password is missing';
}
if (!password2) {
errs.password2 = 'Confirm password is missing';
}
if (!first_name) {
errs.first_name = 'First name is required';
}
if (!last_name) {
errs.last_name = 'Last name is required';
}
if (username.length < 6) {
errs.username = 'Username is too short';
}
if (password1.length < 8) {
errs.password1 = 'Password is too short';
}
if (password1 !== password2) {
errs.password1 = 'Passwords must match';
errs.password2 = 'Passwords must match';
}
if (Object.keys(errs).length) {
handleErrors(errs);
return false;
}
else {
return true;
}
};
const mapStateToProps = state => {
return {
loggedInUser: state.users.loggedInUser,
registerPending: state.users.registerPending,
registerError: state.users.registerError
};
};
const mapDispatchToProps = dispatch => {
return {
registerUser: newUser => {
dispatch(registerUser(newUser));
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(RegisterForm);
When clearInputs is triggered, it should reset inputs to the initial state. Instead nothing is happening. Any help is really appreciated.
EDIT:
Let me further clarify. Each Field in my form is passed a value from inputs (username, password1, etc). When clearInputs is called, it clears the inputs in the hook but it DOES NOT clear the values in the Field.
Your clearInputs function is working as intended. The setInputs function returned by useState is asynchronous, causing your 'AFTER THE SETTING' console log to show the value of inputs before it has been updated.
Basic use of your custom hook is shown to work here.. https://codesandbox.io/s/jznpk7w85w
btw, you should prefix your custom hook name with use.. https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook
About why you log isn't right, one must understand this: Everytime your hook is called (basically on every form render) there is a new clearInputs function created, that has its own version of inputs. So, within the clearInputs function itself, inputs cannot change, because they come from higher up in the scope, the useState.
If you want to notice changes between 2 calls of your hook, you could log inputs just before returning [inputs, ...].
Again, in your hook, you are not calling setInputs, you are defining a clearInputs function that will trigger a state change, that will re-render your component, which will use your hook once again, your hook will read the new value of inputs, and create a new clearInputs function.