Unable to get the updated state in ReactJS login page - javascript

I am new to React JS and have stuck on a problem. I am building a login page where I want to display some error when the user enters invalid credentials. When I enter the correct credentials I am able to login but when I enter the invalid credentials then also I am able to login. On debugging I have found that in mapPropsToState although I get isLoggedIn parameter as false but it is not mapped to props. props still get true here.
My Login Page:
const required = (value) => {
if (!value) {
return (
<div className="alert alert-danger" role="alert">
This field is required!
</div>
);
}
};
const Login = (props) => {
const form = useRef();
const checkBtn = useRef();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [loading, setLoading] = useState(false);
const { isLoggedIn } = useSelector(state => state.auth);
const { message } = useSelector(state => state.message);
const dispatch = useDispatch();
const onChangeUsername = (e) => {
const username = e.target.value;
setUsername(username);
};
const onChangePassword = (e) => {
const password = e.target.value;
setPassword(password);
};
const handleLogin = (e) => {
e.preventDefault();
setLoading(true);
form.current.validateAll();
if (checkBtn.current.context._errors.length === 0) {
dispatch(login(username, password))
.then(() => {
if (props !=null && props.isAuthenticated) {
props.history.push("/home");
}
})
.catch(() => {
setLoading(false);
});
} else {
setLoading(false);
}
};
if (isLoggedIn) {
// return <Redirect to="/home" />;
}
return (
<div className="col-md-12">
<div className="card card-container">
<img
src="//ssl.gstatic.com/accounts/ui/avatar_2x.png"
alt="profile-img"
className="profile-img-card"
/>
<Form onSubmit={handleLogin} ref={form}>
<div className="form-group">
<label htmlFor="username">Username</label>
<Input
type="text"
className="form-control"
name="username"
value={username}
onChange={onChangeUsername}
validations={[required]}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<Input
type="password"
className="form-control"
name="password"
value={password}
onChange={onChangePassword}
validations={[required]}
/>
</div>
<div className="form-group">
<button className="btn btn-primary btn-block" disabled={loading}>
{loading && (
<span className="spinner-border spinner-border-sm"></span>
)}
<span>Login</span>
</button>
</div>
{message && (
<div className="form-group">
<div className="alert alert-danger" role="alert">
{message}
</div>
</div>
)}
<CheckButton style={{ display: "none" }} ref={checkBtn} />
</Form>
</div>
</div>
);
};
Login.propTypes = {
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired
}
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isLoggedIn
})
export default connect(mapStateToProps, { login })(Login);
my login and logout function in action:
export const login = (username, password) => (dispatch) => {
return AuthServicee.login(username, password).then(
(data) => {
if(data.success) {
userService.getUserDetails(username).then((data) => {
localStorage.setItem("user", JSON.stringify(data.data));
dispatch({
type: LOGIN_SUCCESS,
payload: { user: data },
});
return Promise.resolve();
},(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: message,
});
return Promise.reject();
}).catch(err => {
dispatch({
type: LOGIN_FAIL,
});
});;
} else {
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: data.error,
});
}
},
(error) => {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
dispatch({
type: LOGIN_FAIL,
});
dispatch({
type: SET_MESSAGE,
payload: message,
});
return Promise.reject();
}
);
};
export const logout = () => (dispatch) => {
AuthServicee.logout();
dispatch({
type: LOGOUT,
});
};
reducer class:
const user = JSON.parse(localStorage.getItem("user"));
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case LOGIN_SUCCESS:
return {
...state,
isLoggedIn: true,
user: payload.user,
};
case LOGIN_FAIL:
return {
...state,
isLoggedIn: false,
user: null,
};
case LOGOUT:
return {
...state,
isLoggedIn: false,
user: null,
};
default:
return state;
}
}
can someone please help me here?
Edit: I get correct values in my state variable in mapStateToProps but somehow when I try to use it in then function of my dispatch call, I still get props.isAuthenticated as true. Although it should have become false as I have updated it in mapStateToProps.

If you're being redirected to the /home I would look into your validation functions first, because as you say the pass invalid values. What does this function do if you have invalid inputs form.current.validateAll();?

Related

useReducer state change is one character delay at onChange

When I try to use useReducer as state management but the problem arises when I use onChange with the reducer it is one character delay. There is a useEffect that depends on the character changes to turn the validity value true or false.
But it works fine with the onBlur.
What should be the cause of this delay?
How can I solve this issue?
import React, { useState, useEffect, useReducer, useContext } from "react";
import AuthContext from "../store/auth-context";
import Button from "../UI/Button";
import Card from "../UI/Card";
import Input from "../UI/Input";
const reducerFn = (state, action) => {
if (action.type === "Email") {
return {
...state,
emailState: action.value,
};
} else if (action.type === "Email_IsValid") {
return {
...state,
isEmailValid: true,
};
} else if (action.type === "Email_IsInValid") {
return {
...state,
isEmailValid: false,
};
} else if (action.type === "Password") {
return {
...state,
passwordState: action.value,
};
} else if (action.type === "Password_IsValid") {
return {
...state,
isPasswordIsValid: true,
};
} else if (action.type === "Password_IsInValid") {
return {
...state,
isPasswordIsValid: false,
};
}
return { emailState: "", isEmailValid: false, passwordState: "", isPasswordIsValid: false };
};
const Login = () => {
const [formState, dispatchFn] = useReducer(reducerFn, { emailState: "", isEmailValid: false, passwordState: "", isPasswordIsValid: false });
const { emailState, isEmailValid, passwordState, isPasswordIsValid } = formState;
const [isEmailValidated, setIsEmailValidated] = useState(null);
const [isPasswordIsValidated, setIsPasswordIsValidated] = useState(null);
const authCtx = useContext(AuthContext);
useEffect(() => {
if (isEmailValid) setIsEmailValidated(true);
if (isPasswordIsValid) setIsPasswordIsValidated(true);
}, [isEmailValid, isPasswordIsValid]);
const onEmailChangeHandler = ({ target }) => {
console.log(isEmailValid, isEmailValidated);
dispatchFn({ type: "Email", value: target.value });
if (emailState.includes("#")) {
dispatchFn({ type: "Email_IsValid" });
} else {
dispatchFn({ type: "Email_IsInValid" });
setIsEmailValidated(false);
}
};
const onEmailBlurHandler = () => {
if (emailState.includes("#")) {
dispatchFn({ type: "Email_IsValid" });
} else {
dispatchFn({ type: "Email_IsInValid" });
setIsEmailValidated(false);
}
};
const onPasswordChangeHandler = ({ target }) => {
dispatchFn({ type: "Password", value: target.value });
if (passwordState.length > 7) {
dispatchFn({ type: "Password_IsValid" });
} else {
dispatchFn({ type: "Password_IsInValid" });
setIsPasswordIsValidated(false);
}
console.log(isPasswordIsValid);
};
const onPasswordBlurHandler = () => {
if (passwordState.length > 7) {
dispatchFn({ type: "Password_IsValid" });
} else {
dispatchFn({ type: "Password_IsInValid" });
setIsPasswordIsValidated(false);
}
};
const onFormSubmit = (e) => {
e.preventDefault();
if (isEmailValid === false) setIsEmailValidated(false);
else if (isPasswordIsValid === false) setIsPasswordIsValidated(false);
else if (isEmailValid && isPasswordIsValid) authCtx.onLogin(emailState, passwordState);
};
return (
<Card>
<form>
<Input
id="email"
label="E-Mail"
type="email"
onChange={onEmailChangeHandler}
onBlur={onEmailBlurHandler}
value={emailState}
isValidated={isEmailValidated}
warningText="Please enter a valid email; must contains '#'"
/>
<Input
id="password"
label="Password"
type="password"
onChange={onPasswordChangeHandler}
onBlur={onPasswordBlurHandler}
value={passwordState}
isValidated={isPasswordIsValidated}
warningText="Password must have 8 characters long"
/>
<Button label="Login" onClick={onFormSubmit} classes={`bgRed bgWider`}></Button>
</form>
</Card>
);
};
export default Login;
You'd have a much simpler time not using reducers here at all, like so:
import React from "react";
const Login = () => {
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const [shouldValidateEmail, setShouldValidateEmail] = React.useState(false);
const [shouldValidatePassword, setShouldValidatePassword] = React.useState(false);
const emailIsValid = email.includes("#");
const passwordIsValid = password.length > 7;
const errors = [
shouldValidateEmail && !emailIsValid ? "Email invalid" : null,
shouldValidatePassword && !passwordIsValid ? "Password invalid" : null,
]
.filter(Boolean)
.join(", ");
return (
<form>
Email
<input
id="email"
type="email"
onChange={(e) => setEmail(e.target.value)}
onBlur={() => setShouldValidateEmail(true)}
value={email}
/>
<br />
Password
<input
id="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
onBlur={(e) => setShouldValidatePassword(true)}
value={password}
/>
<br />
{errors ? <>Errors: {errors}</> : null}
</form>
);
};
export default function App() {
const [resetKey, setResetKey] = React.useState(0);
return (
<div className="App">
<Login key={resetKey} />
<hr />
<button onClick={() => setResetKey((k) => k + 1)}>Reset</button>
</div>
);
}
However, if you really do want to use a reducer, just couple the validation state to when the state is changed:
function validateEmail(email) {
return email.includes("#");
}
function validatePassword(password) {
return password.length > 7;
}
const initialState = {
emailState: "",
isEmailValid: false,
passwordState: "",
isPasswordValid: false,
};
const reducerFn = (state, action) => {
if (action.type === "Email") {
return {
...state,
emailState: action.value,
isEmailValid: validateEmail(action.value),
};
} else if (action.type === "Password") {
return {
...state,
passwordState: action.value,
isPasswordValid: validatePassword(action.value),
};
}
return initialState;
};
const Login = () => {
const [{ emailState, isEmailValid, passwordState, isPasswordValid }, dispatchFn] = React.useReducer(
reducerFn,
initialState,
);
const errors = [
emailState && !isEmailValid ? "Email invalid" : null,
passwordState && !isPasswordValid ? "Password invalid" : null,
]
.filter(Boolean)
.join(", ");
return (
<form>
Email
<input
id="email"
type="email"
onChange={(e) => dispatchFn({ type: "Email", value: e.target.value })}
value={emailState}
/>
<br />
Password
<input
id="password"
type="password"
onChange={(e) => dispatchFn({ type: "Password", value: e.target.value })}
value={passwordState}
/>
<br />
{errors ? <>Errors: {errors}</> : null}
</form>
);
};

TypeError: Failed to fetch from API server React JS

So I get TypeError: Failed to fetch on React JS code. It says the error is on index.js line 5 and 15, My API server is up and running so i don't think its the API server. Whenever i click on submit, I get empty data undefined which should have been an array and typeerror. It was supposed to pass me an array when I clicked on submit in console
index.js
import {API} from "../../backend"
import {cartEmpty} from "../../core/helper/cartHelper"
export const signup = user => {
return fetch(`${API}users/`, {
method: "POST",
headers: {
Accecpt : "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(user)
})
.then((response) => {
return response.json()
})
.catch(err => console.log(err))
}
export const signin = user => {
const formData = new FormData()
for (const name in user) {
formData.append(name, user[name])
}
return fetch(`${API}user/login`, {
method: "POST",
body: FormData
})
.then(response => {
return response.json()
})
.catch(err => console.log(err))
}
export const authenticate = (data, next) => {
if (typeof window !== undefined) {
localStorage.setItem("jwt", JSON.stringify(data))
next()
}
}
export const isAuthenticated = () => {
if (typeof window == undefined) {
return false
}
if (localStorage.getItem("jwt")) {
return JSON.parse(localStorage.getItem("jwt"))
} else {
return false
}
}
export const signout = next => {
const userId = isAuthenticated() && isAuthenticated().user.id
if (typeof window !== undefined) {
localStorage.removeItem("jwt")
cartEmpty(() => {})
//next()
return fetch(`${API}user/logout/${userId}`, {
method: "GET"
})
.then(response => {
console.log("Signout success")
next()
})
.catch(err => console.log(err))
}
}
Signup.js
import React, {useState} from 'react'
import Base from '../core/Base'
import {Link} from "react-router-dom"
import { signup } from '../auth/helper'
import { stringify } from 'query-string'
const Signup = () => {
const [values, setValues] = useState({
name: "",
email: "",
pasword: "",
error: "",
success: false
})
const {name, email, password, error, success} = values
const handleChange = name => event => {
setValues({...values, error: false, [name]: event.target.value})
}
const onSubmit = (event) => {
event.preventDefault()
setValues({...values, error: false})
signup({name, email, password})
.then(data => {
console.log("DATA", data)
})
.catch(err => console.log(err))
}
const signUpForm = () => {
return (
<div className="row">
<div className="col-md-6 offset-sm-3 text-left">
<form>
<div className="form-group">
<label className="text-light">Name</label>
<input onChange={handleChange("name")} value={name} className="form-control" type="text"></input>
</div>
<div className="form-group">
<label className="text-light">Email</label>
<input onChange={handleChange("email")} value={email} className="form-control" type="email"></input>
</div>
<div className="form-group">
<label className="text-light">Password</label>
<input onChange={handleChange("password")} value={password} className="form-control" type="password"></input>
</div>
<button onClick={onSubmit} className="btn btn-success btn-block">Submit</button>
</form>
</div>
</div>
)
}
return (
<Base title="Sign up Page" description="A signup">
{signUpForm()}
<p className="text-white text-center">{JSON.stringify(values)}</p>
</Base>
)
}
export default Signup

Displaying error message and redirecting from the component

Current Status: Not Solved
I'm just looking to show an error message to see if the user email already exists. For that, I have checkValidUser function which returns the responses back. Now, I am accessing those responses as message in RegistrationForm component.
For now I'm able to console the message here in the component, as seen below.
So, the flow is like:
When the user enters the email, if email is already there in the database, a toastError should be shown saying "User already exists". If the email is not present in the db, a toastSuccess should be showing saying "You are succesfully registered", and at the same time redirected to the login page.
I'm not able to figure out how to accomplish this.
RegistrationForm.js
import React, { Component } from "react";
import { registerUser, checkValidUser } from "../actions/userActions";
import { connect } from "react-redux";
import validator from "validator";
import { Link } from "react-router-dom";
import { toastError, toastInfo, toastSuccess } from "../../utils/toastify";
class RegistrationForm extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
email: "",
password: "",
};
}
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};
componentDidUpdate(prevProps) {
console.log(" inside componentDidUpdate");
console.log("cdu", "prevProp=>", prevProps.message, "newProp=>", this.props.message);
}
handleSubmit = async (event) => {
event.preventDefault();
const { username, email, password } = this.state;
const registrationData = {
username: this.state.username,
email: this.state.email,
password: this.state.password,
};
if (!username || !email || !password) {
return toastError("Credentials should not be empty");
}
if (username.length < 6) {
return toastError("Username should be greater than 6 characters.");
}
if (!validator.isEmail(email)) {
return toastError("Invalid email.");
}
if (password.length < 6) {
return toastError("Password must contain 6 characters.");
}
await this.props.dispatch(checkValidUser(email));
const message = this.props.message;
console.log("message in component=>", message)
}
render() {
console.log("render");
const isRegistrationInProgress = this.props.isRegistrationInProgress;
return (
<div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input
onChange={this.handleChange}
name="username"
value={this.state.username}
className="input"
type="text"
placeholder="Username"
/>
<span className="icon is-small is-left">
<i className="fas fa-user"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left has-icons-right">
<input
onChange={this.handleChange}
name="email"
value={this.state.email}
className="input"
type="email"
placeholder="Email"
/>
<span className="icon is-small is-left">
<i className="fas fa-envelope"></i>
</span>
</p>
</div>
<div className="field">
<p className="control has-icons-left">
<input
onChange={this.handleChange}
name="password"
value={this.state.password}
className="input"
type="password"
placeholder="Password"
/>
<span className="icon is-small is-left">
<i className="fas fa-lock"></i>
</span>
</p>
</div>
<div className="field">
<div className="control">
{isRegistrationInProgress ? (
<button className="button is-success is-loading">Sign Up</button>
) : (
<button onClick={this.handleSubmit} className="button is-success">
Sign up
</button>
)}
<Link to="/login">
<p className="has-text-danger">
Already have an account? Sign In
</p>
</Link>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
isRegistrationInProgress: state.registration.isRegistrationInProgress,
message: state.registration.message,
};
};
export default connect(mapStateToProps)(RegistrationForm);
registerUser action
export const registerUser = (registrationData, redirect) => {
console.log("registrationData", registrationData)
return async (dispatch) => {
dispatch({ type: "REGISTRATION_STARTS" })
try {
const res = await axios.post(
`${baseUrl}/users/register`,
registrationData
)
dispatch({
type: "REGISTRATION_SUCCESS",
data: { user: res.data.user },
})
toastSuccess("Successfully registered")
redirect()
} catch (err) {
dispatch({
type: "REGISTRATION_ERROR",
data: { error: err },
})
}
}
}
checkValidUser controller function
checkValidUser: async (req, res, next) => {
console.log("inside check valid user controller")
const { email } = req.params
console.log(email)
try {
const user = await User.findOne({ email })
if (user) {
return res.status(200).json({ message: "User already exists" })
} else {
return res.json({ message: "User does not exists" })
}
} catch (error) {
return next(error)
}
}
registerUser controller function:
registerUser: async (req, res, next) => {
try {
var { username, email, password } = req.body
if (password) {
const salt = bcrypt.genSaltSync(10)
password = bcrypt.hashSync(password, salt)
}
if (!username || !email || !password) {
return res
.status(400)
.json({ message: "Username, email and password are must" })
}
if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invaid email" })
}
if (password.length < 6) {
return res
.status(400)
.json({ message: "Password should be of at least 6 characters" })
}
const user = await User.create({ username, email, password })
if (!user) {
return res.status(404).json({ error: "No user found " })
}
return res.status(200).json({ user })
} catch (error) {
return next(error)
}
}
registration reducer
const initialState = {
isRegistrationInProgress: false,
isRegistered: false,
registrationError: null,
user: {},
isValidating: false,
isValidated: false,
validationError: null,
message: "",
}
const registration = (state = initialState, action) => {
switch (action.type) {
case "REGISTRATION_STARTS":
return {
...state,
isRegistrationInProgress: true,
registrationError: null,
}
case "REGISTRATION_SUCCESS":
return {
...state,
isRegistrationInProgress: false,
registrationError: null,
isRegistered: true,
user: action.data,
}
case "REGISTRATION_ERROR":
return {
...state,
isRegistrationInProgress: false,
registrationError: action.data.error,
isRegistered: false,
user: {},
}
case "CHECK_VALID_USER_STARTS":
return {
...state,
isValidating: true,
isValidated: false,
validationError: null,
}
case "CHECK_VALID_USER_SUCCESS":
return {
...state,
isValidating: false,
isValidated: true,
message: action.data.message,
}
case "CHECK_VALID_USER_ERROR":
return {
...state,
validationError: action.data.error,
}
default:
return state
}
}
export default registration
checkValidUser action
export const checkValidUser = (email) => {
return async (dispatch) => {
dispatch({ type: "CHECK_VALID_USER_STARTS" })
try {
const res = await axios.get(`${baseUrl}/users/checkValidUser/${email}`)
console.log("message in action=>", res.data)
dispatch({
type: "CHECK_VALID_USER_SUCCESS",
data: { message: res.data.message },
})
} catch (err) {
dispatch({
type: "CHECK_VALID_USER_ERROR",
data: { error: "Something went wrong" },
})
}
}
}
In your mapStateToProps since your component needs the isValidated current state when email action is dispatched:
const mapStateToProps = (state) => {
return {
isRegistrationInProgress: state.registration.isRegistrationInProgress,
message: state.registration.message,
isValidated: state.checkValidUser.isValidated
};
};
You should check your response message after dispatching in your checkValidUser action. Because the response would be 200 if call succeeds but it doesn’t mean validated could be email exists or not.
export const checkValidUser = (email) => {
return async (dispatch) => {
dispatch({ type: "CHECK_VALID_USER_STARTS" })
try {
const res = await axios.get(`${baseUrl}/users/checkValidUser/${email}`)
console.log("message in action=>", res.data)
if(res.data.messsage="User does not exists"){//you can just return
dispatch({ // success or failed
type: "CHECK_VALID_USER_SUCCESS",
data: { message: res.data.message },
})
toastSuccess("Success");
redirect();
}else{
toastError("Email exists")
}
} catch (err) {
dispatch({
type: "CHECK_VALID_USER_ERROR",
data: { error: "Something went wrong" },
})
}
}
}

Redux state updates but component state not changing from first time

I am developing a login/register system using Redux,Hooks and Axios the action should fetch the backend and return a session to be updated in my reducer , after that I am trying to console.log(session) from my component the first time it is {} empty 'The initial state of session' is consoled the second time it is updated , I checked my redux state and everything works good and the state is updated from the first time so not a redux issue .The problem is in my component as I need it to wait for the Redux finishing and then console.log() , I tried to setTime() but it waits for the given time and after that It console the initial state {} again.
My code:
Component:
The problem is in the Redirect() function
import { LoginAction } from "../../Redux/Actions";
import { useForm } from "react-hook-form";
import { connect } from "react-redux";
const Login = (props) => {
let history = useHistory();
const alert = useAlert();
const { handleSubmit, register, errors } = useForm();
const onSubmit = (data) => {
const userData = {
username: data.username.toUpperCase(),
password: data.password,
};
props.login(userData);
setTimeout(1000, Redirect());
};
const Redirect = () => {
if (props.session.user) {
console.log(props.session);
sessionStorage.setItem("storedSession", props.session.user.username);
history.push("/dashboard");
} else {
alert.show(<div style={{ size: "10px" }}>{props.session.error}</div>);
}
};
return (
<div className="login">
<div className="login-form">
<h3 className="title">Sign In</h3>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="text"
placeholder="Enter your username"
name="username"
id="username"
ref={register({ required: true })}
/>
{errors.username && errors.username.type === "required" && (
<p className="error-before-submit">This is required</p>
)}
<input
id="password"
placeholder="Enter your password"
name="password"
type="password"
ref={register({ required: true })}
/>
{errors.password && errors.password.type === "required" && (
<p className="error-before-submit">This is required</p>
)}
<input
className="btn"
type="submit"
ref={register({ required: true })}
/>
<a href="/register" className="register-link">
Create an account
</a>
</form>
</div>
</div>
);
};
const mapStateToProps = (state) => ({
session: state.Session,
});
const mapDispatchToProps = (dispatch) => ({
login: (user) => dispatch(LoginAction(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Login);
Reducer:
const initialState = {
Session: {},
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case Register:
return {
...state,
Session: action.payload,
};
case Login:
return {
...state,
Session: action.payload,
};
default:
return state;
}
};
export default rootReducer;
Action:
export function LoginAction(user) {
return (dispatch) => {
Axios.post(
"/api/login",
{
username: user.username,
password: user.password,
},
{
headers: { "Access-Control-Allow-Origin": "*" },
withCredentials: true,
crossdomain: true,
}
).then((res) => {
dispatch({
type: Login,
payload: res.data,
});
});
};
}
How can I make my component take the updated state not the initial state FROM FIRST CLICK ??
You can leverage useEffect hook instead of using timeout
const {session} = props;
useEffect(()=>{
Redirect()
},[session])

How to successfully register users within redux using firebase auth

I keep getting the error undefined when registering a user.
I'm not sure if react is obtaining the states information correctly. Maybe it could be the onChange value, or maybe im missing something else.
I referenced this
How to implement Firebase authentication with React Redux?
but still unsure, what the error can be.
It shows that the user has been sign up on the backend like this.
Demo
https://stackblitz.com/edit/react-h9ekc4
Actions
export const onEmailSignUpChangeAction = value => ({
type: EMAIL_SIGN_UP_CHANGE,
email: value
})
export const onPasswordSignUpChangeAction = value => ({
type: PASSWORD_SIGN_UP_CHANGE,
password: value
})
export const onEmptySignUpEmailClick = () => ({
type: 'EMPTY_SIGN_UP_EMAIL'
})
export const onEmptySignUpPasswordClick = () => ({
type: 'EMPTY_SIGN_UP_PASSWORD'
})
export const signUp = () => (dispatch, getState) => {
const {signUpAuth} = getState();
if (signUpAuth.emailSignUp === '') {
dispatch(onEmptySignUpEmailClick())
}
if (signUpAuth.passwordSignUp === '') {
dispatch(onEmptySignUpPasswordClick())
}
else {
firebaseAuth.createUserWithEmailAndPassword(signUpAuth.emailSignUp, signUpAuth.passwordSignUp)
.then(() => console.log('signUpok'))
.catch( function (error) {
let errorCode = error.code;
let errorMessage = error.message;
alert(errorMessage)
});
}
}
SignUp.js
import React, { Component } from 'react';
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { signUp, onEmailSignUpChangeAction, onPasswordSignUpChangeAction } from '../actions/';
class SignUp extends Component {
state = {
email: "",
password: ""
}
// onChange = (e) =>{
// this.setState({
// [e.target.name] : e.target.value
// })
// }
handleSubmit = (e) => {
e.preventDefault();
const register = this.props.signUp();
console.log(register);
(register === true) && this.props.history.push('/');
console.log(this.state)
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<h1>Sign Up</h1>
<form onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input
type="email"
className="form-control"
id="email"
onChange={this.props.onEmailSignUpChangeAction}
aria-describedby="emailHelp"
value={this.props.emailSignUp}
placeholder="Enter email" />
<small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
</div>
<div className="form-group">
<label htmlFor="exampleInputPassword1">Password</label>
<input
type="password"
className="form-control"
id="password"
value={this.props.passwordSignUp}
onChange={this.props.onPasswordSignUpChangeAction}
placeholder="Password" />
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({
user: state.auth.user,
emailSignUp: state.signUpAuth.emailSignUp,
passwordSignUp: state.signUpAuth.passwordSignUp
})
const mapDispatchToProps = (dispatch) => ({
signUp: () => dispatch(signUp()),
onEmailSignUpChangeAction: (event) => dispatch(onEmailSignUpChangeAction(event.target.value)),
onPasswordSignUpChangeAction: (event) => dispatch(onPasswordSignUpChangeAction(event.target.value)),
});
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(SignUp));
Reducers.js
const initialState = {
emailSignUp: '',
passwordSignUp: '',
errorTextEmailSignUp: '',
errorTextPasswordSignUp: ''
}
export default (state = initialState, action) => {
switch (action.type) {
case EMAIL_SIGN_UP_CHANGE:
return {
...state,
emailSignUp: action.email
}
case PASSWORD_SIGN_UP_CHANGE:
return {
...state,
passwordSignUp: action.password
}
case EMPTY_SIGN_UP_EMAIL:
return {
...state,
errorTextEmailSignUp: 'This field is required'
}
case EMPTY_SIGN_UP_PASSWORD:
return {
...state,
errorTextPasswordSignUp: 'This field is required'
}
default:
return state
}
}
If you want to pass this.props.emailSignUp and this.props.passwordSignUp into your signUp function you could try:
export const signUp = (email, password) => { return (dispatch) => {
if (email === '') {
dispatch({ type: EMPTY_SIGN_UP_EMAIL })
}
else if (password === '') {
dispatch({ type: EMPTY_SIGN_UP_PASSWORD })
}
else {
firebaseAuth.createUserWithEmailAndPassword(email, password)
.then(() => console.log('signUpok'))
.catch( function (error) {
let errorCode = error.code;
let errorMessage = error.message;
alert(errorMessage)
});
}
}
}
Then call your function this.props.signUp(this.props.emailSignUp, this.props.passwordSignUp)
You are assigning signUp method's return to subscribed variable but that method does return nothing.
Since its execution is asynchronous, you may need to dispatch an action that will cause a reducer to store the created user in the state when creation has succeeded, then make use of a selector for retrieving that user for instance.

Categories

Resources