Initialstate not updated after success login - javascript

I just learned how react-redux works.
I created a backend with express js and used axios in the fronted(authentication). After I login, it just gives me a success status.but I can't understand why after success login the state still like initialState in reducer
login.js
import React,{ useState} from 'react'
import { connect, useDispatch } from 'react-redux'
import {login} from '../../actions/authentication'
import './auth.css'
import LoginForm from './LoginForm'
import {Link} from 'react-router-dom'
const Login = ({history, login}) => {
const dispatch = useDispatch();
const [formData, setFormData] = useState({
email: "",
password:""
});
const hundleChange = (e) => {
setFormData({ ...formData, [e.target.name]: e.target.value })
}
const hundleSubmit = (e) => {
e.preventDefault();
dispatch(login(formData));
history.push("/");
}
return(
<div>
<div className="forms">
<h2>Login</h2>
<LoginForm email = {formData.email}
password = {formData.password}
hundleSubmit = {hundleSubmit}
hundleChange = {hundleChange}/>
</div>
<p>Do you not have account</p>
<Link to="/register">Sign Up</Link>
</div>
)
}
const mapStateToProps = state => (
{
auth : state.auth
}
)
export default connect (mapStateToProps, {login})(Login);
authentication.js
import axios from 'axios'
import { AUTH_FAILURE, LOGIN_USER_SUCCESS } from './types'
//login user
export const login = (formData) => async (dispatch) =>{
//formData ={email, password}
try {
const res = await axios.post("/api/auth/login", formData)
dispatch({
type: LOGIN_USER_SUCCESS,
payload: res.data
});
console.log("here data", dispatch.payload)
} catch (error) {
console.dir(error);
const response = error.response.data;
if(Array.isArray(response)){
response.forEach((err) =>
{
alert(err.msg);
});
}
dispatch({
type: AUTH_FAILURE
});
}
}
authReducer.js
import {LOGIN_USER_SUCCESS} from '../actions'
const initialState = {
token: null,
user:null,
isLoading: false,
isAuth: false
};
export default (state = initialState, {type, payload}) => {
switch(type) {
case LOGIN_USER_SUCCESS:
return {...state,
user: payload.user,
token: payload.token,
isLoading: false,
isAuth: true
};
default:
return state;
}
};
redux_devtools1
redux_devtools2

I found the solution,the problem was with the import
import {LOGIN_USER_SUCCESS} from '../actions/types'
import axios from 'axios'
import {LOGIN_USER_SUCCESS} from '../actions/types'
//login user
export const login = (formData) => async (dispatch) =>{
//formData ={email, password}
try {
const res = await axios.post("/api/auth/login", formData)
dispatch({
type: LOGIN_USER_SUCCESS,
payload: res.data
});
console.log("here data", dispatch.payload)
} catch (error) {
console.dir(error);
const response = error.response.data;
if(Array.isArray(response)){
response.forEach((err) =>
{
alert(err.msg);
});
}
dispatch({
type: AUTH_FAILURE
});
}
}

Related

Another React Hook Question - Cannot read properties of undefined (reading 'params')

I really seem to have trouble with react and hooks. I know that the problem could be do to react-dom. I am watching a tutorial that does it with 5 and im using 6. I looked at the documentation, but I honestly can't find the issue. It worked for my homepage, but now the product page doesn't work and I honestly couldn't find anything on past answers about this problem. Does anybody have an idea?? Thanks!
import React, {useState, useEffect} from 'react'
import { Link } from 'react-router-dom'
import { Row, Col, Image, ListGroup, Button, Card } from 'react-bootstrap'
import Rating from '../components/Rating'
import Loader from '../components/Loader'
import Message from '../components/Message'
import { useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { listProductDetails } from '../actions/productActions'
function ProductScreen({match}) {
const dispatch = useDispatch()
const productDetails = useSelector(state => state.productDetails)
const {loading, error, product} = productDetails
useEffect(() =>{
dispatch(listProductDetails(match.params.id))
},[])
ProductActions.js:
export const listProductDetails = (id) => async(dispatch) => {
try {
dispatch({type: PRODUCT_DETAILS_REQUEST})
const { id } = useParams();
const {data} = await axios.get(`/api/products/${id}`)
dispatch({
type: PRODUCT_DETAILS_SUCCESS,
payload: data
})
} catch (error) {
dispatch({
type: PRODUCT_DETAILS_FAIL,
payload: error.response && error.response.data.message
? error.response.data.message
: error.message,
})
}
}
productReducer.js:
export const productDetailsReducer = (state={product:{reviews:[]}},action) => {
switch(action.type){
case PRODUCT_DETAILS_REQUEST:
return {loading:true, ...state}
case PRODUCT_DETAILS_SUCCESS:
return {loading:false, product:action.payload}
case PRODUCT_DETAILS_FAIL:
return {loading:false, error: action.payload}
default:
return state
}
}
store.js:
import { legacy_createStore as createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import {productListReducer,productDetailsReducer} from './reducers/productReducers'
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
})
const initialState = {}
const middleware = [thunk]
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware)))
export default store
export const listProductDetails = (id) => async(dispatch) => {
try {
dispatch({type: PRODUCT_DETAILS_REQUEST})
const { id } = useParams();
[...]
You are passing id as argument of listProductDetails then you are redefining it inside the function.
Move const { id } = useParams(); into ProductScreen and pass it as argument to function instead of using match.
Also listProductDetails returns an async function that takes dispatch as argument. So don't call dispatch(listProductDetails(id)) but instead call listProductDetails(id)(dispatch).
Also calling dispatch within the api is not a good idea. You generally want action function to return something to be dispatched within a React Component.
function ProductScreen() {
const dispatch = useDispatch();
const { id } = useParams();
const productDetails = useSelector((state) => state.productDetails);
const { loading, error, product } = productDetails;
useEffect(() => {
const fetchData = async (id) => {
dispatch({ type: PRODUCT_DETAILS_REQUEST });
dispatch(await listProductDetails(id));
};
fetchData(id);
}, [dispatch, id]);
return <div>APP</div>;
}
In ProductActions.js
export const listProductDetails = async (id) => {
try {
const { data } = await axios.get(`/api/products/${id}`);
return {
type: PRODUCT_DETAILS_SUCCESS,
payload: data,
};
} catch (error) {
return {
type: PRODUCT_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
};
}
};

produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'

I got following error at onAuthStateChanged method at store.dispatch(userActions.setUser(null));
Error: [Immer] produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '[object Object]'
I am trying to change to firebase authentication from jwt. So custom jwt authentication is using redux. Now when I call firebase's signOut(auth) method, onAuthStateChanged method give me this error. May I know how to write immerable object for user.
user_reducer.js
import produce from "immer";
import * as Action_Constants from "../actions/user_actions";
const initialState = null;
export const UserReducer = produce((state = initialState, action) => {
switch (action.type) {
case Action_Constants.SET_USER: {
return action.user;
}
case Action_Constants.FETCH_USER_COMPLETED: {
return action.user;
}
default:
return state;
}
});
user_actions.js
export const FETCH_USER = "FETCH_USER";
export const FETCH_USER_COMPLETED = "FETCH_USER_COMPLETED";
export const SET_USER = "SET_USER";
export const actionCreators = {
fetchUser: (id) => ({
type: FETCH_USER,
id,
}),
fetchUserCompleted: (user) => ({
type: FETCH_USER_COMPLETED,
user,
}),
setUser: (user) => ({
type: SET_USER,
user,
}),
};
I have deleted other firebase functions to simply the file.
auth_provider.jsx
import React, { useState, useEffect, useContext, createContext } from "react";
import { useLocation, Navigate } from "react-router-dom";
import { signIn, signUp } from "../helpers/gql_auth_helpers";
import paths from "../routes/paths";
import { store } from "../store/configure_store";
import { actionCreators as userActions } from "../store/actions/user_actions";
import { auth } from "../helpers/init-firebase";
import {
onAuthStateChanged,
signOut,
} from "firebase/auth";
const AuthContext = createContext(null);
let accessToken = "";
export const getAccessToken = () => accessToken;
export const setAccessToken = (token) => {
accessToken = token;
};
export const AuthProvider = ({ user: usr, children }) => {
const [user, setUser] = useState(usr);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
if (user) {
setUser(user);
setAccessToken(user.getIdToken(true));
store.dispatch(userActions.setUser(user));
} else {
setUser(null);
setAccessToken(null);
store.dispatch(userActions.setUser(null));
}
});
return () => {
unsubscribe();
};
}, []);
async function logout() {
return signOut(auth);
}
const value = {
user,
accessToken,
logout,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
return useContext(AuthContext);
};

I am trying to implement Api in my demo react redux application When I click on event my Api call twice

HI I am trying to implement fake API in my react redux demo application when I fire event I am getting response twice I don't no where I did wrong here I am sharing my code please review it
User.tsx
import React, { useState } from 'react';
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "./Store";
import { GetUser } from './actions/UserActions';
function User() {
const dispatch = useDispatch();
const [userName, setUserName] = useState("");
const userState = useSelector((state: RootState) => state.user);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setUserName(event.target.value);
const handleSubmit = () => dispatch(GetUser(userName));
return (
<div >
<input type="text" onChange={handleChange} />
<button onClick={handleSubmit}>Search</button>
</div>
);
}
export default User;
UserActions.tsx
import { Dispatch } from "redux"
import {
UserDispatchTypes,
// UserType,
USER_FAIL,
USER_LOADING,
USER_SUCCESS,
} from "./UserActionTypes"
import axios from "axios"
export const GetUser = (user: string) => async (
dispatch: Dispatch<UserDispatchTypes>
) => {
try {
dispatch({
type: USER_LOADING,
})
console.log("USer Action load")
const articleAPI = "https://reqres.in/api/articles"
const article = { title: "React Hooks POST Request Example" }
const res1 = await axios.post(
articleAPI,
article
// , {
)
console.log(res1.data, "res1 response")
dispatch({
type: USER_SUCCESS,
payload: res1.data,
})
} catch (e) {
dispatch({
type: USER_FAIL,
})
}
}
UserREducer
import {
UserDispatchTypes,
UserType,
USER_FAIL,
USER_LOADING,
USER_SUCCESS,
} from "../actions/UserActionTypes"
interface DefaultStateI {
loading: boolean
user?: UserType
}
const defaultState: DefaultStateI = {
loading: false,
}
const userReducer = (
state = defaultState,
action: UserDispatchTypes
): DefaultStateI => {
switch (action.type) {
case USER_FAIL:
return {
...state,
loading: false,
}
case USER_LOADING:
return {
...state,
loading: true,
}
case USER_SUCCESS:
return {
...state,
loading: false,
user:res1.data
}
default:
return state
}
}
export default userReducer
UseerActionTypes
export const USER_LOADING = 'USER_LOADING'
export const USER_FAIL = 'USER_FAIL'
export const USER_SUCCESS = 'USER_SUCCESS'
export type UserType = { userData: UserAbility[] }
export type UserAbility = {
user: {
name: string
mobile_No: number
}
}
export interface UserLoading {
type: typeof USER_LOADING
}
export interface UserFail {
type: typeof USER_FAIL
}
export interface UserSuccess {
type: typeof USER_SUCCESS,
payload: UserType
}
export type UserDispatchTypes = UserLoading | UserFail | UserSuccess
code are working fine but when i see browser console api call twice

When I combine reducers for some reason my auth reducer is not attaching itself to the store, how is that?

See image explanation of problem
Problem:
The auth reducer is not getting recognized in the global store when imported & attempted to combine with the other reducers. Where the other reducers such as alert, profile & post have been recognized by the global store.
 
Auth Reducer:
Github: https://github.com/DariusRain/Pluto/blob/master/client/src/redux/modules/auth.js
import api from "../../utils/api";
import { setAlert } from "./alert";
// Action Types
export const REGISTER_SUCCESS = "PLUTO/AUTH/REGISTER_SUCCESS";
export const REGISTER_FAIL = "PLUTO/AUTH/REGISTER_FAIL";
export const LOGIN_SUCCESS = "PLUTO/AUTH/LOGIN_SUCCESS";
export const LOGIN_FAIL = "PLUTO/AUTH/LOGIN_FAIL";
export const USER_LOADED = "PLUTO/AUTH/USER_LOADED";
export const AUTH_ERROR = "PLUTO/AUTH/ERROR";
export const LOGOUT = "PLUTO/AUTH/LOGOUT";
export const ACCOUNT_DELETED = "PLUTO/AUTH/ACCOUNT_DELETED";
// Reducer
const initialState = {
token: localStorage.getItem("token"),
isAuthenticated: null,
loading: true,
user: null,
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case USER_LOADED:
return {
...state,
isAuthenticated: true,
loading: false,
user: payload,
};
case REGISTER_SUCCESS:
return {
...state,
...payload,
isAuthenticated: true,
loading: false,
};
case LOGIN_SUCCESS:
return {
...state,
...payload,
isAuthenticated: true,
loading: false,
};
case ACCOUNT_DELETED:
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
};
case REGISTER_FAIL:
case AUTH_ERROR:
case LOGOUT:
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
};
default:
return state;
}
};
// Action Creators
export const loadUser = () => async (dispatch) => {
try {
const res = await api.get("/auth");
dispatch({
type: USER_LOADED,
payload: res.data,
});
} catch (err) {
dispatch({
type: AUTH_ERROR,
});
}
};
// Register User
export const register = (formData) => async (dispatch) => {
try {
const res = await api.post("/users", formData);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: REGISTER_FAIL,
});
}
};
// Login User
export const login = (email, password) => async (dispatch) => {
try {
const res = await api.post("/auth", { email, password });
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: LOGIN_FAIL,
});
}
};
// Logout
export const logout = () => async (dispatch) => dispatch({ type: LOGOUT });
 
Combine Reducers
Github: https://github.com/DariusRain/Pluto/blob/master/client/src/redux/modules/index.js
import {
combineReducers
} from "redux";
import alert from "./alert";
import post from "./post";
import profile from "./profile";
import auth from "./auth";
export default combineReducers({
alert,
profile,
post,
auth,
});
 
Store
Github: https://github.com/DariusRain/Pluto/blob/master/client/src/redux/index.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './modules';
import setAuthToken from '../utils/setAuthToken';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
let currentState = store.getState();
store.subscribe(() => {
let previousState = currentState;
currentState = store.getState();
if (previousState.auth.token !== currentState.auth.token) {
const token = currentState.auth.token;
setAuthToken(token);
}
});
export default store;
Repository:
Pluto: https://github.com/DariusRain/Pluto
 
Help appreciated! 😁 -DariusRain
You have a circular dependency issue. When you initialize store you import it:
App.js -> import store from './redux';
store initialization:
a) redux/modules/auth.js -> import api from "../../utils/api"
b) utils/api.js -> import store from '../redux' -> here you get crash
I'd suggest split reducers and action creators into separate files

Redux Dev tools updating. Console not updating with state changes or data

Ive been trying to do this with react hooks and the useSelector/useDispatch. What happens is, I am able to see the data and state change in the Redux DevTools however, when logging to the console, I either get an empty array or undefined. I am also not able to render the data to the screen expectedly.
Posts Component
import React, {useState, useEffect} from 'react'
import PropTypes from 'prop-types'
import {useSelector, useDispatch} from 'react-redux'
import {getPosts} from '../actions/postActions'
const Posts = props =>{
const dispatch = useDispatch();
const postData = useSelector(state=> state.items, []) || []; //memoization?
const [items, setItems] = useState(postData)
console.log(postData);
useEffect(() => {
dispatch(getPosts());
}, []);
return(
<h1>{postData[0]}</h1>
)
}
export default Posts
ACTIONS
import {GET_POSTS, NEW_POSTS} from '../actions/types'
export const getPosts =()=> dispatch =>{
//fetch
console.log('fetching')
const url = 'https://jsonplaceholder.typicode.com/posts/'
fetch(url)
.then(res => res.json())
.then(posts=> dispatch({type: GET_POSTS, payload: posts}))
}
reduxDevTools image
I think the problem is coming from this line:
const postData = useSelector(state=> state.items, []) || [];
If you want postData to initially be an array, it's best to set it as an array in your reducer.
Working example (click Posts tab to make API call):
actions/postActions.js
import api from "../utils/api";
import * as types from "../types";
export const getPosts = () => async dispatch => {
try {
dispatch({ type: types.POSTS_FETCH });
const res = await api.get("posts");
dispatch({
type: types.POSTS_SET,
payload: res.data
});
} catch (err) {
dispatch({
type: types.POSTS_FAILED_FETCH,
payload: err.toString()
});
}
};
reducers/postsReducer.js
import * as types from "../types";
const initialState = {
data: [],
error: "",
isLoading: true
};
export default (state = initialState, { type, payload }) => {
switch (type) {
case types.POSTS_FETCH:
return initialState;
case types.POSTS_SET:
return {
...state,
data: payload,
error: "",
isLoading: false
};
case types.POSTS_FAILED_FETCH:
return {
...state,
error: payload,
isLoading: false
};
default:
return state;
}
};
containers/FetchPostsHooks/index.js
import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { getPosts } from "../../actions/postActions";
import Spinner from "../../components/Spinner";
import DisplayPosts from "../../components/DisplayPosts";
const Posts = () => {
const dispatch = useDispatch();
const { isLoading, data, error } = useSelector(state => state.posts, []);
useEffect(() => {
dispatch(getPosts());
}, [dispatch]);
return isLoading ? (
<Spinner />
) : error ? (
<p>{error}</p>
) : (
<DisplayPosts data={data} />
);
};
export default Posts;

Categories

Resources