Redux saga failing to get data from API - reactjs

I have a react application where I have a username and password field which is then submitted to the backend(PHP) for creating and starting session. This is the code for Login.js:
import React, { useEffect, useState } from 'react'
import { useDispatch } from 'react-redux'
import { getResult } from "./redux/FormSlice"
import { useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
function Login() {
const [username, setUsername] = useState("");//state variable for username
const [password, setPassword] = useState("");/state variable for password
let { result } = useSelector(state => state.FormSliceReducer)//result stores whether the authentication is successful or not
const navigate = useNavigate();
//the following useEffect is for reacting to changes whether the login is successful or not
useEffect(() => {
switch (result) {
case "success": {
const url = "/main/" + username
navigate(url)
break;
}
case "Invalid password": {
alert("Invalid password")
break;
}
case "Invalid email": {
alert("Invalid email")
break;
}
}
}, [result])
const dispatch = useDispatch();
const formSubmitted = (event) => {
event.preventDefault();
dispatch(getResult({username, password}))//when I submit the form I send username and password to the particular reducer and saga.
}
return (
<div>
<h1>Welcome to form</h1>
<form onSubmit={formSubmitted} method='POST' className='form'>
<label>Username: </label>
<input type="text" name="username" value={username} onChange={(event) => setUsername(event.target.value)}/>
<label>Password: </label>
<input type="password" name='password' value={password} onChange={(event) => setPassword(event.target.value)}/>
<input type="submit" value="Submit" name='submit'/>
</form>
</div>
)
}
export default Login
Whenever I click on submit I get the following error:
Error
FormSlice.js: Since I am using redux-toolkit I use createSlice function
import { createSlice } from "#reduxjs/toolkit";
const initialState = {
result: ""
}
const slice = createSlice({
name: "FormSlice",
initialState,
reducers: {
getResult: (state, action) => {
},
setResult: (state, action) => {
//the message from the PHP backend comes here
state.result = action.payload.result[0].message
},
}
})
export const { getResult, setResult } = slice.actions
export default slice.reducer;
FormSaga.js:
import { put, takeEvery } from "redux-saga/effects"
function* getResult1(action) {
let url = "http://localhost:8787/php/form/form.php?username=" + action.payload.username + "&password=" + action.payload.password;
let result = yield fetch(url)
result = yield result.json()
yield put({type: "FormSlice/setResult", payload: result})
}
function* authSaga() {
yield takeEvery("FormSlice/getResult", getResult1);
}
export default authSaga;
Please comment if more information is needed.

Related

Authorization form in react js

File UserStore.js
import { makeAutoObservable } from "mobx";
export default class UserStore {
constructor() {
this._isAuth = false;
this._user = {};
makeAutoObservable(this);
}
setIsAuth(bool) {
this._isAuth = bool;
}
setUser(user) {
this._user = user;
}
get isAuth() {
return this._isAuth;
}
get user() {
return this._user;
}
}
File Auth.js
import React, {useContext, useState} from 'react';
import { NavLink, useLocation } from 'react-router-dom';
import './../styles/auth.css';
import { LOGIN_ROUTE, REGISTRATION_ROUTE } from '../utils/consts';
import { observer } from 'mobx-react-lite';
import { Context } from '../index';
import { login, registration } from '../http/userApi';
const Auth = observer(() => {
const {user} = useContext(Context)
const location = useLocation()
const isLogin = location.pathname === LOGIN_ROUTE
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const click = async() => {
try {
let data;
if (isLogin) {
data = await login(email, password);
} else {
data = await registration(email, password);
}
user.setUser(data)
user.setIsAuth(true)
} catch (e) {
alert(e.response.data.message)
}
}
return (
<section className='section auth'>
<div className='container auth__wrapper'>
<div className='card'>
<form>
<h2>{isLogin ? 'Авторизация' : 'Регистрация'}</h2>
<input
type='email'
placeholder='Введите ваш email...'
value={email}
onChange={e => setEmail(e.target.value)}
/>
<input
type='password'
placeholder='Введите ваш пароль...'
value={password}
onChange={e => setPassword(e.target.value)}
/>
<div className='btnandreg'>
{isLogin ?
<div className='text'>
Нет аккаунта? <NavLink className='NavLink underline' to={REGISTRATION_ROUTE}>Зарегистрируйтесь!</NavLink>
</div>
:
<div className='text'>
Есть аккаунт? <NavLink className='NavLink underline' to={LOGIN_ROUTE}>Войдите!</NavLink>
</div>
}
<button onClick={() => click()} className='form__btn'>{isLogin ? 'Войти' : 'Регистрация'}</button>
</div>
</form>
</div>
</div>
</section>
);
});
export default Auth;
File userApi.js
import { $authHost, $host } from "./index";
import jwt_decode from 'jwt-decode';
export const registration = async (email, password) => {
const {data} = await $host.post('api/user/registration', { email, password, role: 'ADMIN' })
return jwt_decode(data.token)
}
export const login = async (email, password) => {
const {data} = await $host.post('api/user/login', { email, password })
return jwt_decode(data.token)
}
export const check = async () => {
const response = await $host.post('api/auth/registration')
return response
}
Authorization passes, if in the case of successful authorization write alert(), it works, the problem is that the value of the variable isAuth does not change, and I assume that user.setUser(data) also does not work, but I can not be sure, I need to change the value of the variable isAuth to true, and also triggered user.setUser(data), repeat code runs successfully to this point, the problem is in these two lines or may be in another file`

React: A non-serializable value was detected in the state

I'm trying to make a simple login/logout feature in my app using firebase auth rest API, I'm using redux to let user log in and logout, the user get registered perfectly in the firebase but when I hit Signup & Login button of the form, I'm getting this error 👇
With redux toolkit I'm wondering what's going wrong with my initialState of login function.
Here is my code: -
//login-store.js
const { configureStore, createSlice } = require("#reduxjs/toolkit");
const userAuth = createSlice({
name: "login",
initialState: {
token: "",
isLoggedIn: false,
login: (token) => {},
logout: () => {},
},
reducers: {
logginOut(state) {
state.isLoggedIn = false;
state.logout = () => {
localStorage.removeItem("userLoginToken");
};
},
loggingIn(state) {
state.isLoggedIn = true;
state.token = localStorage.getItem("userLoginToken");
state.login = (token) => {
return localStorage.setItem("userLoginToken", token);
};
},
},
});
const authStore = configureStore({
reducer: userAuth.reducer,
});
export const userAuthAction = userAuth.actions;
export default authStore;
And here I'm having my login and signup feature. Also there is one more problem, whenever I click New User button below the submit button, I immediately get the alert popup (written with comment below) I don't know how am I sending fetch request while switching the form...
//Login.js
import React, { useRef, useState } from "react";
import { useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { userAuthAction } from "../store/login-store";
import classes from "./pages.module.css";
export default function Login() {
const dispatch = useDispatch();
const [isLogin, setIsLogin] = useState(true);
const navigate = useNavigate();
const emailInput = useRef();
const passwordInput = useRef();
const switchAuthTextHandler = () => {
setIsLogin((prevState) => !prevState);
};
const loginAuthHandler = (e) => {
e.preventDefault();
const enteredEmailValue = emailInput.current.value;
const enteredPasswordValue = passwordInput.current.value;
let authUrl;
if (isLogin) {
// when logging in
authUrl =
"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaSyB3Mbv38Ju8c9QedQzqX3QvufTCOXhkU0c";
} else {
// when signing up
authUrl =
"https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaSyB3Mbv38Ju8c9QedQzqX3QvufTCOXhkU0c";
}
fetch(authUrl, {
method: "POST",
body: JSON.stringify({
email: enteredEmailValue,
password: enteredPasswordValue,
returnSecureToken: true,
}),
headers: {
"Content-type": "application/json",
},
})
.then((res) => {
if (res.ok) {
return res.json();
} else {
return res.json().then((data) => {
// getting alert popup immediately after switching the form
alert(data.error.message);
});
}
})
.then((data) => {
dispatch(userAuthAction.loggingIn(data.idToken));
navigate("/");
console.log(data);
})
.catch((err) => {
console.error(err.message);
});
};
return (
<div className={classes.loginWrapper}>
<form onSubmit={loginAuthHandler}>
<h4>{isLogin ? "Login" : "Signup"}</h4>
<div className={classes.form_group}>
<label htmlFor="email">Email</label>
<input type="email" id="email" ref={emailInput} />
</div>
<div className={classes.form_group}>
<label htmlFor="password">Password</label>
<input type="password" id="password" ref={passwordInput} />
</div>
<div className={classes.form_group}>
<button type="submit">{isLogin ? "Login" : "Signup"}</button>
</div>
<div className={classes.form_group}>
<button className={classes.newUser} onClick={switchAuthTextHandler}>
{isLogin ? "New User?" : "Already have account"}
</button>
</div>
</form>
</div>
);
}
Error while siging up the new user:-
We should not store function references in the redux store. They are not serializable, and states should be serializable in redux state.

update user password using react-router-dom v6

i want to implement update user password form using react-router-dom v6 but this code is not working..
please please.. put your suggestion or explain me about my mistakes on this code.
userReducer.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_RESET,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const profileReducer = (state = {}, action) => {
switch (action.type) {
case UPDATE_PASSWORD_REQUEST:
return {
...state,
loading: true,
};
case UPDATE_PASSWORD_SUCCESS:
return {
...state,
loading: false,
isUpdated: action.payload,
};
case UPDATE_PASSWORD_FAIL:
return {
...state,
loading: false,
error: action.payload,
};
case UPDATE_PASSWORD_RESET:
return {
...state,
isUpdated: false,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
userAction.js
import {
UPDATE_PASSWORD_REQUEST,
UPDATE_PASSWORD_SUCCESS,
UPDATE_PASSWORD_FAIL,
CLEAR_ERRORS,
} from "../Constants/userConstant";
export const updatePassword = (passwords) => async (dispatch) => {
try {
dispatch({ type: UPDATE_PASSWORD_REQUEST });
const config = { headers: { "Content-Type": "application/json" } };
const { data } = await axios.put(
`/api/v1/password/update`,
passwords,
config
);
dispatch({ type: UPDATE_PASSWORD_SUCCESS, payload: data.success });
} catch (error) {
dispatch({
type: UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
}
};
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
store.js
import {createStore,combineReducers,applyMiddleware} from 'redux';
import thunk from "redux-thunk";
import {composeWithDevTools} from "redux-devtools-extension";
import { profileReducer } from './Reducers/userReducer';
const reducer = combineReducers({
profile:profileReducer,
})
let initialState = {};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
UpdatePassword.js
import React, { Fragment, useState, useEffect } from "react";
import "./UpdatePassword.css";
import Loader from "../Loader/Loader";
import { useDispatch, useSelector } from "react-redux";
import { clearErrors, updatePassword } from "../../Actions/userAction";
import { UPDATE_PASSWORD_RESET } from "../../Constants/userConstant";
import {useNavigate} from 'react-router-dom'
const UpdatePassword = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { error, isUpdated, loading } = useSelector((state) => state.profile);
const [oldPassword, setOldPassword] = useState("");
const [newPassword, setNewPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const updatePasswordSubmit = (e) => {
e.preventDefault();
const myForm = new FormData();
myForm.set("oldPassword", oldPassword);
myForm.set("newPassword", newPassword);
myForm.set("confirmPassword", confirmPassword);
dispatch(updatePassword(myForm));
};
useEffect(() => {
if (error) {
alert(error);
dispatch(clearErrors());
}
if (isUpdated) {
alert("Profile Updated Successfully");
navigate("/account");
dispatch({
type: UPDATE_PASSWORD_RESET,
});
}
}, [dispatch, error, isUpdated]);
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
{/* <MetaData title="Change Password" /> */}
<div className="updatePasswordContainer">
<div className="updatePasswordBox">
<h2 className="updatePasswordHeading">Update Profile</h2>
<form
className="updatePasswordForm"
onSubmit={updatePasswordSubmit}
>
<div className="loginPassword">
<input
type="password"
placeholder="Old Password"
required
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="New Password"
required
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
/>
</div>
<div className="loginPassword">
<input
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
</div>
<input
type="submit"
value="Change"
className="updatePasswordBtn"
/>
</form>
</div>
</div>
</Fragment>
)}
</Fragment>
);
};
export default UpdatePassword;
i want to make a form where user update user password.but due to any mistake this form is not working...

How to redirect to protected route after successful login?

I am working on an authentication system using react at front. I am storing token which comes from my backend server to localStorage and i want user to redirect to dashboard page when there is a token present in localStorage. Every time i login using correct credentials i get token but not redirecting to dashboard page. But when i change route in url it works. I am using react context api.
AuthContext.js
import { createContext } from "react";
const AuthContext = createContext();
export default AuthContext;
AuthState.js
import React, { useReducer, useState } from "react";
import AuthContext from "./AuthContext";
import { SUCCESS_LOGIN } from "../types";
import AuthReducers from "./AuthReducers";
import Axios from "axios";
const AuthState = ({ children }) => {
//setting up initial state for authcontext
const initialState = {
userAuth: null,
userLoading: false,
token: localStorage.getItem("token"),
errors: null,
};
const [state, dispatch] = useReducer(AuthReducers, initialState);
//logging user in
const loginUser = async (userData) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
try {
//posting to api
const res = await Axios.post("/api/user/login", userData, config);
console.log(res.data);
dispatch({
type: SUCCESS_LOGIN,
payload: res.data,
});
} catch (error) {
console.log(error.response);
}
};
return (
<AuthContext.Provider
value={{
userAuth: state.userAuth,
errors: state.errors,
token: state.token,
loginUser,
}}
>
{children}
</AuthContext.Provider>
);
};
export default AuthState;
AuthReducers.js
import { SUCCESS_LOGIN } from "../types";
export default (state, action) => {
switch (action.type) {
case SUCCESS_LOGIN:
const token = action.payload.token;
localStorage.setItem("token", token);
return {
...state,
userAuth: true,
userLoading: true,
errors: null,
token: localStorage.getItem("token"),
};
default:
return state;
}
};
Login.js
import React, { useState, useContext } from "react";
import { useHistory } from "react-router-dom";
import { Button, Form, FormGroup, Label, Input, FormText } from "reactstrap";
import styles from "./login.module.css";
import AuthContext from "../../context/AuthContext/AuthContext";
const Login = (props) => {
//grabbing states from authContext
const { loginUser, userAuth } = useContext(AuthContext);
let history = useHistory();
const [credentials, setCredentials] = useState({
email: "",
password: "",
});
//pulling email and password from state
const { email, password } = credentials;
//method to handle changes on input fields
const handleChange = (e) => {
const { name, value } = e.target;
setCredentials({
...credentials,
[name]: value,
});
};
//method to handle login when user submits the form
const handleLogin = (e) => {
e.preventDefault();
loginUser({ email, password });
console.log(userAuth);
if (userAuth) {
history.push("/dashboard");
}
};
return (
<Form onSubmit={handleLogin}>
<FormGroup>
<Label for="email">Email</Label>
<Input
type="email"
name="email"
value={email}
placeholder="Enter your email"
onChange={handleChange}
/>
</FormGroup>
<FormGroup>
<Label for="password">Password</Label>
<Input
type="password"
name="password"
value={password}
placeholder="Enter password"
onChange={handleChange}
/>
</FormGroup>
<Button className={styles.loginBtn}>Submit</Button>
</Form>
);
};
export default Login;
PrivateRoute.js
import React, { useContext } from "react";
import { Route, Redirect } from "react-router-dom";
import AuthContext from "../../context/AuthContext/AuthContext";
const PrivateRoute = ({ component: Component, ...rest }) => {
const { token, userAuth } = useContext(AuthContext);
return (
<div>
<Route
{...rest}
render={(props) =>
token ? <Component {...props} /> : <Redirect to="/" />
}
/>
</div>
);
};
export default PrivateRoute;
You need to do this in Login.js.
useEffect(() => {
if (userAuth) {
history.push("/dashboard");
}
},[userAuth,history])
Its happening because when you do handleLogin click functionality you dont have userAuth at that time as true(its taking previous value). Because context update change is not available in handleLogin function . Instead track userAuth in useEffect
If you are trying to redirect the user after successful login via your handleLogin() function, it won't work because of this:
if (userAuth) {
history.push("/dashboard");
}
The above will not run, because userAuth won't change until the component re-renders, after which, the function will have finished executing. You should either return something from your loginUser() action, and redirect based on its return of a successful "login", or implement conditional rendering inside of the Login component, like so:
return userAuth
? <Redirect to="/dashboard" /> // redirect if userAuth == true
: (
// your Login JSX // if userAuth == false, render Login form
)

UseEffect not fired when redux props change caused by async api call with redux-thunk

I have a functional login page connected with redux, I'm firing an async event onSubmit that will trigger the emailLogin action, I am using useEffect to detect the change of the isLoading prop to see whether login finished or not. If login success, the redux store should have the user object, if failed, the user should remain null.
The question is, I know that the login is success, which should triggered the change of isLoading, the parameter that decide whether the useEffect, however, the useEffect is not fired. Also, the console.log('done'); after the line await emailLogin(authData); is never fired. Ssomething is wrong.
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';
import { emailLogin } from '../actions/index';
function Login({ user, isLoading, emailLogin }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const history = useHistory();
useEffect(() => {
console.log('useEffect fired', user, isLoading); //<-----This does not fire after login success
if (user) {
history.push('/protected_home');
}
}, [isLoading]);
const submitEmailLoginForm = async (e) => {
e.preventDefault();
const authData = { email, password };
await emailLogin(authData);
console.log('done'); // <------- This is never fired
};
return (
<div>
<h2>Login</h2>
<Link to="/">back</Link>
<form onSubmit={submitEmailLoginForm}>
<label>
email:
<input
type="text"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</label>
<label>
password:
<input
type="text"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
const mapStateToProps = (state) => ({
user: state.user,
isLoading: state.isLoading
});
const mapDispatch = {
emailLogin: emailLogin
};
export default connect(mapStateToProps, mapDispatch)(Login);
My action file:
import axios from 'axios';
export const authActions = {
EMAIL_LOGIN_START: '##EMAIL_LOGIN_START',
EMAIL_LOGIN_SUCCESS: '##EMAIL_LOGIN_SUCCESS'
};
export const emailLogin = ({ email, password }) => async (dispatch) => {
dispatch({ type: authActions.EMAIL_LOGIN_START });
try {
const response = await axios.post('http://localhost:5001/api/auth', {
email: email,
password: password
});
dispatch({
type: authActions.EMAIL_LOGIN_SUCCESS,
payload: {
user: { ...response.data }
}
});
} catch (error) {
console.log('Should dispatch api error', error.response);
}
};
My Reducer:
import { authActions } from '../actions/index';
const initialState = {
user: null,
isLoading: false
};
const userReducer = (state = initialState, action) => {
switch (action.type) {
case authActions.EMAIL_LOGIN_START:
return { ...state, isLoading: true };
case authActions.EMAIL_LOGIN_SUCCESS:
console.log('Reducer check => Login is success'); //<-----this line is printed
return { ...state, user: action.payload.user, isLoading: false };
default:
return state;
}
};
export default userReducer;
In the reducer, I see that the success action is actually triggered by checking the console.log(). Also in the redux dev tool, I can actually see that the login is success and the isLoading prop has changed :
This solve my problem
const mapStateToProps = (state) => ({
user: state.userReducer.user,
isLoading: state.userReducer.isLoading
});

Resources