I'm building a site with basic auth with Spring. I use Redux. I'm sending a request to "/auth" in the backend. After successfully logging in, i get those:
enter image description here
As you can see I have successfully logged in.
But i still can not be authenticated. I did not refresh the page i did nothing but this is the console output. By the way, postman is working fine.
enter image description here
This is part of apiCalls:
import axios from "axios";
export const signup = (body) => {
return axios.post('/users', body);
};
export const login = creds => {
return axios.post('/auth', {}, {auth:creds});
};
export const getMarketItemsSortByDate = () => {
return axios.get("/market/last");
}
This is configureStore:
import {createStore, applyMiddleware,compose} from 'redux';
import authReducer from './authReducer';
import SecureLS from 'secure-ls';
import thunk from 'redux-thunk';
const secureLS = new SecureLS();
const getStateFromStorage = () => {
const hoaxAuth = secureLS.get('hoax-auth');
let stateInLocalStorage = {
isLoggedIn:false,
username:undefined,
mail:undefined,
balance:undefined,
password:undefined
};
if(hoaxAuth){
stateInLocalStorage = hoaxAuth;
}
return stateInLocalStorage;
}
const updateStateInStorage = newState => {
secureLS.set('hoax-auth', newState);
}
const configureStore = () => {
const initialState = getStateFromStorage();
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(authReducer, initialState, composeEnhancers(applyMiddleware(thunk)));
store.subscribe(()=> {
updateStateInStorage(store.getState());
})
return store;
}
export default configureStore;
This is authActions :
import * as ACTIONS from "./Constants";
import {login} from '../api/apiCalls';
export const logoutSuccess = () => {
return {
type: ACTIONS.LOGOUT_SUCCESS
};
}
export const loginSuccess = authState => {
return {
type: ACTIONS.LOGIN_SUCCESS,
payload: authState
};
};
export const loginHandler = credentials => {
return async function(dispatch) {
const response = await login(credentials);
const authState = {
...response.data,
password: credentials.password,
};
console.log(authState);
dispatch(loginSuccess(authState));
return response;
};
};
And this is authReducer:
import * as ACTIONS from './Constants';
const defaultState = {
isLoggedIn:false,
username:undefined,
mail:undefined,
balance:undefined,
password:undefined
}
const authReducer = (state= { ...defaultState},action) => {
if(action.type === ACTIONS.LOGOUT_SUCCESS){
return defaultState;
} else if(action.type === ACTIONS.LOGIN_SUCCESS){
return {
...action.payload,
isLoggedIn:true
}
}
return state;
}
export default authReducer;
Everything is fine with postman so problem should be inside React.js
Make sure to set withCredentials for each requests you made with axios that requires the token as such:
export const login = creds => {
return axios.post('/auth', {}, {auth:creds, withCredentials:true});
};
export const getMarketItemsSortByDate = () => {
return axios.get("/market/last", {}, {withCredentials:true});
}
which allows the access token to be set and to be sent along with the request.
Related
I have this react component where in the user wants to send in a login request but whenever I dispatch the action, even before it is executed the further code in my component is executed.
I've tried making the login request function as async and even tried using await before dispatching the action but it's all been in vain.
Component file:
import React from 'react';
import BaseButton from '../BaseButton/BaseButton';
import { useState } from 'react';
import { userLogin } from '../../redux/auth/authActions';
import axios from 'axios';
import {connect} from 'react-redux'
function Login({ isLoggedIn, userLogin }) {
const [login, setLogin] = useState(true); //to see if the user wats to login or sign up
const [email, setEmail] = useState("");
const [name, setName] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const handleLogin = () => {
let userCredentials = {
email: email,
password: password
}
userLogin(userCredentials); // <------ i want to wait for this to execute before the below code is executed
if (isLoggedIn) {
console.log('im here');
} else {
console.log('wrong credentials');
}
}
const handleSignUp = async () => {
}
return login ? (
<>
{*/ ...some JSX for user input */}
<div className="flex justify-center">
<BaseButton variant={'solid'} onClick = {handleLogin}>Submit</BaseButton>
</div>
{*/ ...some more JSX for user input */}
<>
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.auth.isLoggedIn
}
}
const dispatchStateToProps = (dispatch) => {
return {
userLogin: (userCredentials) => dispatch(userLogin(userCredentials))
}
}
export default connect(mapStateToProps, dispatchStateToProps)(Login);
authActions:
import {
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGIN_FAILURE,
} from './authTypes';
import axios from 'axios';
export const sendLoginRequest = () => {
return {
type: USER_LOGIN_REQUEST,
};
};
export const loginSucccess = () => {
return {
type: USER_LOGIN_SUCCESS,
};
};
export const loginFailure = (error) => {
return {
type: USER_LOGIN_FAILURE,
payload: error,
};
};
export const userLogin = (userCredentials) => {
return (dispatch) => {
try {
dispatch(sendLoginRequest());
axios
.post('http://localhost:3001/auth/login', userCredentials)
.then((data) => {
console.log(data.status);
dispatch(loginSucccess());
})
.catch(err => {
console.log("incorrect credentials");
dispatch(loginFailure('incorrect credentials'));
});
} catch(err) {
dispatch(loginFailure(err.message));
}
};
};
auth reducer file:
import {
USER_LOGIN_REQUEST,
USER_LOGIN_FAILURE,
USER_LOGIN_SUCCESS,
} from './authTypes';
const initialState = {
loading: false,
isLoggedIn: false,
error: ''
};
const authReducer = (state = initialState, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return {
...state,
loading: true
}
case USER_LOGIN_SUCCESS: return{
...state,
loading: false,
isLoggedIn: true,
}
case USER_LOGIN_FAILURE: return{
...state,
loading: false,
isLoggedIn: false,
error: action.payload
}
default: return state;
}
};
export default authReducer;
my store file:
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from '../rootReducer';
const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
export default store;
root reducer:
import {combineReducers} from 'redux';
import authReducer from './auth/authReducer';
const rootReducer = combineReducers({
auth: authReducer
});
export default rootReducer;
The userLogin action isn't declared async nor does it return a Promise so this is why your handleLogin handler isn't able to wait for it to complete.
Convert userLogin to an async action function.
export const userLogin = (userCredentials) => async (dispatch) => {
try {
dispatch(sendLoginRequest());
const data = await axios.post('http://localhost:3001/auth/login', userCredentials);
console.log(data.status);
dispatch(loginSucccess());
return true; // <-- return resolved value
} catch(err) {
dispatch(loginFailure(err.message));
return false; // <-- return resolved value
}
};
Convert handleLogin to an async function so it can await the dispatched action to resolve. Note that handleLogin won't, and can't see any updated isLoggedIn state value from Redux while it has a current value closed over in scope from the time it was called.
const handleLogin = async () => {
const userCredentials = { email, password };
const authSuccess = await userLogin(userCredentials);
if (authSuccess) {
console.log('I'm here');
} else {
console.log('wrong credentials');
}
};
use async await or then catch in handleLogin function and also do not forgot to add return in userLogin and sub functions
`const handleLogin = async () => {
await userLogin(userCredentials);
if(isLoggedIn) {
console.log('here');
}
}`
or use then
`userLogin(userCredentials).then(() => { if(isLoggedIn){
console.log('here');
}});`
I have an App where I use Ads, and if the user has Premium access, those ads should be off when launching the app. For that I used Redux. I use Firebase auth, and the user object contains an entry called "hasPremium". Now when redux loads on the root (App.js), the auth object is null and the premium is always false, after like 2-3 secs the auth becomes not null, but by that time the initialState is false.
Any smart way to wait for the auth, and only after that create the InitialState for Redux?
App.js
import { Provider } from "react-redux";
import thunk from "redux-thunk";
import { createStore, applyMiddleware } from "redux";
var middlewares = applyMiddleware(thunk);
const store = createStore(reducers, middlewares)
export default function App() {
return (
<>
<Provider store={store}>
<>{isReady ? <AuthNavigation/> : <LoadingScreen/>}</>
</Provider>
</>
);
}
admob.reducer.js
const AD_ON = "AD_ON";
const AD_OFF = "AD_OFF";
async function checkPremium(){
let hasPremium = await checkPremiumForUserAsync()
return hasPremium
}
const initialState = {
ad_status: checkPremium() ?? false, // change this depending on hasPremium from Firebase auth
};
export default (ad_status = initialState, { type }) => {
switch (type) {
case AD_ON:
return { ad_status: true };
case AD_OFF:
return { ad_status: false };
default:
return ad_status;
}
};
admob.action.js
export const ToggleAdOn = () => ({
type: AD_ON,
});
export const ToggleAdOff = () => ({
type: AD_OFF,
});
export const ToggleAds = (ads_state) => {
return async (dispatch) => {
if (ads_state === true) {
dispatch(ToggleAdOn());
} else {
dispatch(ToggleAdOff());
}
};
};
I'm trying to use a combination of a slice react redux to store my token credentials with a dispatch action to update the state in a call to my API.
Finally in the login component i was trying to verify if somehow that token state changed to verify if user can access.
But this condition in my component loginHandler function:
if(auth){ history.push('/wallet') }
Seems to be always empty.
This is my redux slice:
import {createSlice} from "#reduxjs/toolkit";
const authSlice = createSlice({
name: 'auth',
initialState: {
token: {
access: '',
refresh: ''
}
},
reducers: {
login(state, action){
state.token = action.payload
},
logout(state){
state.token.access = ''
state.token.refresh = ''
}
}
})
export const authActions = authSlice.actions
export default authSlice
This is my action:
import http from "../http-common";
import {authActions} from "./auth-slice";
export const login = (data) => {
return async (dispatch) => {
const loginRequest = async () => {
const response = await http.post('/api/token/login', data);
return response.data;
};
try {
const sessionsData = await loginRequest();
console.log(sessionsData)
localStorage.setItem("token", JSON.stringify(sessionsData))
console.log(sessionsData)
dispatch(
authActions.login(sessionsData)
)
} catch (error) {
console.log(error)
}
};
}
And inside my component i'm using the dispatch to my action and the useSelector to track changes to my store state:
import React, {useState, useContext} from 'react';
import {useHistory} from "react-router-dom";
import LoginForm from '../../components/forms/LoginFormPage'
import {useSelector, useDispatch} from "react-redux";
import {login} from "../../store/auth-actions";
const Login = () => {
let history = useHistory();
const dispatch = useDispatch();
const [isLoading, setIsLoading] = useState(false)
const auth = useSelector((state) => state.auth.token.access);
const handleLogin = (data) => {
console.log(auth)
setIsLoading(true)
dispatch(login({email: data.email, password: data.password})).then()
console.log(auth)
if(auth){
history.push('/wallet')
}
setIsLoading(false)
}
return (
<LoginForm
onLoginSubmit={handleLogin}
isLoading={isLoading}/>
)
}
export default Login;
Next.js v12.0
next-redux-wrapper.
Whenever I navigate away from the page using the appropriate next/link element and then back again (using another link el) the state is reset to the initial value and so another fetch is executed. What is strange about this is that I have another 'transaction' slice setup in an identical manner except it holds an array of transaction objects and that one is working just fine (navigate away and back and the data is not re-fetched as it persisted in store) code is below any suggestions would be greatly appreciated.
store.js
import { HYDRATE, createWrapper } from "next-redux-wrapper";
import thunkMiddleware from "redux-thunk";
import address from "./address/reducer";
import transactions from "./transaction/reducer";
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
const { composeWithDevTools } = require("redux-devtools-extension");
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const combinedReducer = combineReducers({
transactions,
address,
});
const rootReducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
if (state.address.id){
nextState.address = state.address;
}
return nextState;
} else {
return combinedReducer(state, action);
}
};
const initStore = () => {
return createStore(rootReducer, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore);
address/reducer.js
const addressInitialState = {
id: null,
timestamp: null,
address: null,
balance: null,
received: null,
sent: null,
groupid: null,
last_txs: []
};
export default function reducer(state = addressInitialState, action) {
switch (action.type) {
case addressActionTypes.GET_WALLET_DETAILS:
return {id: action.payload.address, ...action.payload};
default:
return state;
}
}
address/action.js
export const addressActionTypes = {
GET_WALLET_DETAILS: "GET_WALLET_DETAILS",
};
export const getWalletDetails = (address) => {
return async (dispatch) => {
const fetchData = async () => {
const response = await fetch(
`https:someapi.com/api/getaddress/?address=${address}`
);
if (!response.ok) {
throw new Error("Could not fetch address data!");
}
const data = await response.json();
console.log('req sent');
return data;
};
try {
const addressData = await fetchData();
dispatch({
type: addressActionTypes.GET_WALLET_DETAILS,
payload: addressData,
});
} catch (err) {
console.log(err);
}
};
};
pages/[address].js
import { Fragment } from "react";
import Head from "next/head";
import AddressDetails from "../../../components/crypto/rvn/AddressDetails";
import AddressTransactions from "../../../components/crypto/rvn/AddressTransactions";
import { connect } from "react-redux";
import { getWalletDetails } from "../../../store/address/action";
import { wrapper } from "../../../store/store";
function Address(props) {
return (
<Fragment>
<Head>
<title>RVN</title>
<meta name="description" content="RVN Address" />
</Head>
<AddressDetails address={props.addressDetails}></AddressDetails>
<AddressTransactions
transactions={props.addressDetails["last_txs"]}
address={props.addressDetails.address}
></AddressTransactions>
</Fragment>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) => async (context) => {
const state = store.getState();
if(state.address.id === null) {
await store.dispatch(getWalletDetails(context.params.address));
}
else{
return{
props: {
addressDetails: state.address,
}
}
}
}
);
const mapStateToProps = (state) => ({
addressDetails: state.address,
});
export default connect(mapStateToProps, null)(Address);
Solved this by converting this
const initStore = () => {
return createStore(rootReducer, bindMiddleware([thunkMiddleware]));
};
in store.js
which direclty from https://github.com/vercel/next.js/tree/canary/examples/with-redux-thunk
to this
const store = createStore(rootReducer, bindMiddleware([thunkMiddleware]));
const initStore = () => {
return store
};
so it does not reinitialize the store every time the wrapper is used
this is more in line with the documentation at
https://github.com/kirill-konshin/next-redux-wrapper
I'm new to React, Redux and have been following tutorials on the topic. I'm come across a lot of issues that I've been able to resolve but I've come across an issue I can't resolve. I set up store, and can even view it through Chrome's Redux Tools and it show correctly, however when I try and dispatch to the Store, I always get a Cannot read property 'dispatch' of undefined error. I have followed numerous tutorials letter for letter, and am still stuck with the same error message.
Index.Js
import Layout from '../components/layout/Layout';
import Home from '../components/Home';
import { getRooms } from '../redux/actions/roomActions';
import { wrapper } from '../redux/store';
export default function Index() {
return (
<Layout>
<Home />
</Layout>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
async ({ req, store }) => {
await store.dispatch(getRooms(req));
}
);
roomConstants.js
export const ALL_ROOMS_SUCCESS = 'ALL_ROOMS_SUCCESS';
export const ALL_ROOMS_FAIL = 'ALL_ROOMS_FAIL';
export const CLEAR_ERRORS = 'CLEAR_ERRORS';
reducer.js
import { combineReducers } from 'redux';
import { allRoomsReducer } from './roomReducers';
const reducer = combineReducers({
allRooms: allRoomsReducer,
});
export default reducer;
Store.js
import { createStore, applyMiddleware } from 'redux';
import { HYDRATE, createWrapper } from 'next-redux-wrapper';
import thunkMiddleware from 'redux-thunk';
import reducers from './reducers/reducers';
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state,
...action.payload,
};
return nextState;
} else {
return reducers(state, action);
}
};
const initStore = () => {
return createStore(reducer, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore);
roomReducer.js
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
// All rooms reducer
export const allRoomsReducer = (state = { rooms: [] }, action) => {
switch (action.type) {
case ALL_ROOMS_SUCCESS:
return {
roomsCount: action.payload.roomsCount,
resPerPage: action.payload.resPerPage,
filteredRoomsCount: action.payload.filteredRoomsCount,
rooms: action.payload.rooms,
};
case ALL_ROOMS_FAIL:
return {
error: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
roomAcion.js
import axios from 'axios';
import absoluteUrl from 'next-absolute-url';
import {
ALL_ROOMS_SUCCESS,
ALL_ROOMS_FAIL,
CLEAR_ERRORS,
} from '../constants/roomConstants';
//Clear errors
export const clearErrors = () => async (dispatch) => {
return dispatch({
type: CLEAR_ERRORS,
});
};
// Get all rooms
export const getRooms = (req) => async (dispatch) => {
try {
const { origin } = absoluteUrl(req);
const { data } = await axios.get(`${origin}/api/rooms`);
dispatch({
type: ALL_ROOMS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_ROOMS_FAIL,
payload: error.response.data.message,
});
}
};
in index.js, your getServerSideProps function should read this;
export const getServerSideProps = wrapper.getServerSideProps((store) => async ({ req }) => {
await store.dispatch(getRooms(req));})
Use the old version of next-redux-wrapper like 6.0.2