What would be the best practice for private routing? Maybe I'm doing something wrong, but when user logged in I already am redirect to the /login page
And my second question: Which of these versions is better or you have even better idea?
Code:
Auth
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: false,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async () => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
return message
}
})
CASE 1:
App
function App() {
const dispatch = useDispatch()
useEffect(() => {
dispatch(me())
}, [])
const auth = useSelector((state) => state.auth)
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes isAuthenticated={auth.isUserLoggedIn} />
</BrowserRouter>
</div>
)
}
export default App
Routes
export const AppRoutes = ({ isAuthenticated }) => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute isAuthenticated={isAuthenticated}>
<Stats />
</PrivateRoute>
}
/>
...
PrivateRoute
export const PrivateRoute = ({ children, isAuthenticated }) => {
return isAuthenticated ? children : <Navigate to='/login' />
}
CASE 2:
App
function App() {
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
Routes
export const AppRoutes = () => (
<Routes>
<Route
path='/login'
element={<Login />}
/>
<Route
path='/dashboard'
element={
<PrivateRoute>
<Stats />
</PrivateRoute>
}
/>
...
PrivateRoute
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return isUserLoggedIn ? children : <Navigate to='/login' />
}
CASE 1 or CASE 2 is better to approach or maybe you have a better idea?
For these 2 ideas, it redirects very quickly to /login when I go to /dashboard
What I want to achieve is good practice, quick verification, and waiting until we receive a positive response from the backend that the user is authenticated
Team, what do you suggest?
EDIT
New version:
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async ({}, thunkAPI) => {
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
PrivateRoute
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null
return isUserLoggedIn ? (
children
) : (
<Navigate
to='/login'
replace
/>
)
}
App
function App() {
const dispatch = useDispatch()
const { isUserLoggedIn } = useSelector((state) => state.auth)
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me())
}
}, [])
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
)
}
export default App
Store
import { configureStore, getDefaultMiddleware } from '#reduxjs/toolkit'
import authReducer from '../features/auth/authSlice'
export const store = configureStore({
reducer: {
auth: authReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ serializableCheck: false }),
})
Index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
But when I reload the page I go to /login don't stay at the same page like /dashboard how could I cope with that?
Either approach is fine. The issue you are seeing is based on the initial redux state value being used for auth check and redirection prior to the effect running to set the auth state. You will want to hold off on redirecting until the authentication status is determined. Use the state to rendering null or some loading indicator until a user is verified authenticated.
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true, // <-- assume initially loading state from mouting
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isUserLoggedIn = false
})
},
});
Between the two implementations IMO the second is preferred as it leads to less component coupling. I suggest a mix of the two. Check for a user in App and dispatch the action to set the auth state, and check the auth state in the private route.
function App() {
const dispatch = useDispatch();
const { isUserLoggedIn } = useSelector((state) => state.auth);
useEffect(() => {
if (isUserLoggedIn === null) {
dispatch(me());
}
}, []);
return (
<div data-theme={theme}>
<BrowserRouter>
<AppRoutes />
</BrowserRouter>
</div>
);
}
...
export const PrivateRoute = ({ children }) => {
const dispatch = useDispatch()
const { isLoading, isUserLoggedIn } = useSelector((state) => state.auth)
if (isLoading) return null; // <-- or loading spinner, etc...
return isUserLoggedIn ? children : <Navigate to='/login' replace />
}
The same can be achieved with the first version, you'd just need to pass the isLoading state along with the isUserLoggedIn state as props to the PrivateRoute.
I found a problem! Ha ha!
Seems stupid && simple but it started working ->
Redux
const authSlice = createSlice({
name: 'auth',
initialState: {
user: {},
isUserLoggedIn: null,
isLoading: true,
},
reducers: {},
extraReducers: (builder) => {
builder
.addCase(me.pending, (state) => {
state.isLoading = true
})
.addCase(me.fulfilled, (state, action) => {
state.user = action.payload.userData
state.isUserLoggedIn = true
state.isLoading = false
})
.addCase(me.rejected, (state) => {
state.isLoading = false
state.isUserLoggedIn = false
})
},
})
export const me = createAsyncThunk('auth/me', async (_, thunkAPI) => { // <- {} replaced to _ and this is it!
try {
const user = await userService.getUserData()
return { userData: user.data.data }
} catch (error) {
const message =
(error.response && error.response.data && error.response.data.message) ||
error.message ||
error.toString()
thunkAPI.rejectWithValue(message)
return message
}
})
Related
I'm fairly new to redux toolkit so I'm still having a few issues with it!
I'm stuck on this. I couldn't find what to do, how to follow a method. so i need your help.
My only request is to redirect the user to the home page when he logs in successfully, but I couldn't find what to check, where and how. I will share the code pages that I think will help you.
import { createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
import { TokenService } from "./token.service";
export const AuthService = {};
AuthService.userLogin = createAsyncThunk(
"userSlice/userLogin",
async ({ username, password }) => {
console.log(username, password);
try {
const response = await axios.post(
"https://localhost:7163/api/Login",
{ username, password },
{
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
username: typeof username === "string" ? username.toString() : "",
password: typeof password === "string" ? password.toString() : "",
}),
}
);
if (response.data.status) {
const tokenResponse = { accessToken: response?.data?.data?.token };
TokenService.setToken(tokenResponse);
} else {
console.log("false false false");
return response.data.status;
}
return response.data;
} catch (error) {
console.error(error);
}
}
);
import { createSlice } from "#reduxjs/toolkit";
import { AuthService } from "../../services/auth.service";
const userToken = localStorage.getItem("userToken")
? localStorage.getItem("userToken")
: null;
const initialState = {
userToken,
isLoading: false,
hasError: false,
userinfo: null,
};
const userSlice = createSlice({
name: "userSlice",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(AuthService.userLogin.pending, (state, action) => {
state.isLoading = true;
state.hasError = false;
})
.addCase(AuthService.userLogin.fulfilled, (state, action) => {
state.userToken = action.payload;
state.isLoading = false;
state.hasError = false;
})
.addCase(AuthService.userLogin.rejected, (state, action) => {
state.isLoading = false;
state.hasError = true;
});
},
});
export const selectUserToken = (state) => state.userSlice.userToken;
export const selectLoadingState = (state) => state.userSlice.isLoading;
export const selectErrorState = (state) => state.userSlice.hasError;
export default userSlice.reducer;
export default function SignIn() {
const navigate = useNavigate();
const error = useSelector(selectErrorState);
console.log(error);
const [username, setName] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const data = {
username: username,
password: password,
};
const handleNameChange = (event) => {
setName(event.target.value);
};
const handlePasswordChange = (event) => {
setPassword(event.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(username, password);
const response = dispatch(AuthService.userLogin(data))
.unwrap()
.then(() => {
if (response.success) {
navigate("/");
}
});
console.log("RESPONSE", response);
console.log(error);
};
//index.js
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<Routes>
<Route path="login" element={<Login />} />
<Route path="register" element={<Register />} />
<Route path="" element={<Home />} />
<Route path="events" element={<Events />} />
</Routes>
</BrowserRouter>
</Provider>
</React.StrictMode>
);
log
my result from API:
Does it make more sense to use the status property here?
{data: {…}, status: true, exception: null, code: 200, message: null}
code: 200
data: {token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfb…yMzl9.1P25An_PHA9n4dyQ9JRKOjwPWWtQShWgW9In-gqS7Ek'}
exception: null
message: null
status: true
[[Prototype]]: Object
Even if I enter wrong information, the promise always seems to be fulfilled.
I think I need to use token in this process. The user will see his own information. I can get the token somehow, but I couldn't use it after I put it in localstorage.
While on the login page, it struggled with the data returned from the login meta, but I couldn't. I want to know what is the most logical method in such a situation. Thank you in advance for your answers.
I'm trying to implement a Protected Route, which firstly tries to get an authentification(api call) so it can display the Route.
But somehow the state value doesnt change..
Do you got any idea?
const ProtectedRoute = ({ component: Component, ...rest }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const fetch = async () => {
const result = await axios.get("http://localhost:5000/auth/", {
withCredentials: true,
});
if (result.status >= 200 && result.status < 300) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
}
};
useEffect(() => {
fetch();
}, []);
return (
<Route
{...rest}
render={(props) => {
if (isAuthenticated) {
return <Component {...props} />;
} else {
return <Redirect to={"./loginUser"} />;
}
}}
/>
);
};
export default ProtectedRoute;
What you can do is to return something when the api call is still loading. Something like this :
const ProtectedRoute = ({ component: Component, ...rest }) => {
const [isAuthenticated, setIsAuthenticated] = useState(undefined);
const [isLoading, setIsLoading] = useState(false);
const fetch = async () => {
const result = await axios.get("http://localhost:5000/auth/", {
withCredentials: true,
});
if (result.status >= 200 && result.status < 300) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
}
};
useEffect(() => {
setIsLoading(true);
fetch();
setIsLoading(false);
}, []);
return (
<Route
{...rest}
render={(props) => {
if(isLoading) { // do something }
else if (isAuthenticated !== undefined && !isLoading) {
return <Component {...props} />;
} else {
return <Redirect to={"./loginUser"} />;
}
}}
/>
);
};
export default ProtectedRoute;
Building an app with a Django backend and React front end. When I attempt to login I receive a refresh and access token from Django. I would like to store that token in my localstorage and redirect my users to a static profile page if they have received a valid access token.
I am getting the following errors on my console:
Uncaught TypeError: Cannot read properties of undefined (reading 'getState')
at Provider.js:20:1
at mountMemo (react-dom.development.js:15442:1)
The above error occurred in the <Provider> component:
in Provider (at src/index.js:21)
in Router (at src/index.js:20)
Consider adding an error boundary to your tree to customize error handling behavior.
This is my login.js
const Login = () => {
const [username, setUserName] = useState('');
const [password, setPassword] = useState('');
const handleSubmit = e => {
e.preventDefault()
try {
const response = AxiosInstance.post('/api/token/',{
username: username,
password: password
});
console.log('from api/token we get this:')
console.log(response)
AxiosInstance.defaults.headers['Authorization'] = "JWT " + response.access;
localStorage.setItem('access_token', response.access);
localStorage.setItem('refresh_token', response.refresh);
console.log('JWT response.access to refresh: ')
return response;
} catch (error) {
throw error;
}
}
Login.propTypes = {
login: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps, {
login
}) (withRouter(Login));
//export default Login;
LoginActions.js:
export const login = (userData, redirectTo) => dispatch => {
axios
.post("/api/token/", userData)
.then(response => {
const { auth_token } = response.data;
setAxiosAuthToken(auth_token);
dispatch(setToken(auth_token));
dispatch(getCurrentUser(redirectTo));
})
.catch(error => {
dispatch(unsetCurrentUser());
toastOnError(error);
});
};
export const getCurrentUser = redirectTo => dispatch => {
axios
.get("/users/")
.then(response => {
const user = {
username: response.data.username,
email: response.data.email
};
dispatch(setCurrentUser(user, redirectTo));
})
.catch(error => {
dispatch(unsetCurrentUser());
toastOnError(error);
});
};
export const setCurrentUser = (user, redirectTo) => dispatch => {
localStorage.setItem("user", JSON.stringify(user));
dispatch({
type: SET_CURRENT_USER,
payload: user
});
console.log("set user" + redirectTo);
if (redirectTo !== "") {
dispatch(push(redirectTo));
}
};
and Reducer.js
const createRootReducer = history =>
combineReducers({
router: connectRouter(history),
createUser: signupReducer,
auth: loginReducer
});
export default createRootReducer;
My Root.js:
export default ({ children, initialState = {} }) => {
const history = createBrowserHistory();
const middleware = [thunk, routerMiddleware(history)];
const store = createStore(
rootReducer(history),
initialState,
applyMiddleware(...middleware)
);
// check localStorage
if (!isEmpty(localStorage.getItem("token"))) {
store.dispatch(setToken(localStorage.getItem("token")));
}
if (!isEmpty(localStorage.getItem("user"))) {
const user = JSON.parse(localStorage.getItem("user"));
store.dispatch(setCurrentUser(user, ""));
}
return (
<Provider store={store}>
<ConnectedRouter history={history}>{children}</ConnectedRouter>
</Provider>
);
};
Utils.js:
export const setAxiosAuthToken = token => {
if (typeof token !== "undefined" && token) {
// Apply for every request
axios.defaults.headers.common["Authorization"] = "Token " + token;
} else {
// Delete auth header
delete axios.defaults.headers.common["Authorization"];
}
};
export const toastOnError = error => {
if (error.response) {
// known error
toast.error(JSON.stringify(error.response.data));
} else if (error.message) {
toast.error(JSON.stringify(error.message));
} else {
toast.error(JSON.stringify(error));
}
};
export const isEmpty = value =>
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0);
and finally my index.js:
const history = createBrowserHistory();
ReactDOM.render(
<Router history={history}>
<Provider>
<App />
</Provider>
</Router>,
document.getElementById('root')
);
serviceWorker.unregister();
Any ideas as to where I might be going wrong?
You did not pass store to the Provider:
ReactDOM.render(
<Router history={history}>
// Make sure you import store correct
<Provider store={store} >
<App />
</Provider>
</Router>,
document.getElementById('root')
I'm using Jest/Testing-Library to write UI unit tests.
Components are not rendering on the DOM, and the culprit was the component 'RequireScope' which wraps all of the components individually.
In other words, every component returns this:
return ( <RequireScope> // some MUI stuff</RequireScope>
)
This is preventing my components from being rendered in the DOM tree when tested.
This is because RequireScope makes sure to render its children only if authentication goes through.
How can I simulate a logged-in user given the following code?
RequireScope:
import React, { useEffect, useState } from 'react';
import useAuth from 'src/hooks/useAuth';
export interface RequireScopeProps {
scopes: string[];
}
const RequireScope: React.FC<RequireScopeProps> = React.memo((props) => {
const { children, scopes } = props;
const { isInitialized, isAuthenticated, permissions } = useAuth();
const [isPermitted, setIsPermitted] = useState(false);
useEffect(() => {
if (isAuthenticated && isInitialized) {
(async () => {
const hasPermissions = scopes
.map((s) => {
return permissions.includes(s);
})
.filter(Boolean);
if (hasPermissions.length === scopes.length) {
setIsPermitted(true);
}
})();
}
}, [isAuthenticated, isInitialized, scopes, permissions]);
if (isPermitted) {
return <>{children}</>;
}
return null;
});
export default RequireScope;
The ultimate goal is to have 'isPermitted' to be true. In order to do this 'isInitialized, isAuthenticated, permissions' has to be true. We bring these 3 values from useAuth().
useAuth:
import { useContext } from 'react';
import AuthContext from '../contexts/JWTContext';
const useAuth = () => useContext(AuthContext);
export default useAuth;
JWTContext:
const handlers: Record<string, (state: State, action: Action) => State> = {
INITIALIZE: (state: State, action: InitializeAction): State => {
const { isAuthenticated, permissions, user } = action.payload;
return {
...state,
isAuthenticated,
isInitialized: true,
permissions,
user,
};
},
LOGIN: (state: State): State => {
return {
...state,
isAuthenticated: true,
};
},
LOGOUT: (state: State): State => ({
...state,
isAuthenticated: false,
permissions: [],
}),
};
const reducer = (state: State, action: Action): State =>
handlers[action.type] ? handlers[action.type](state, action) : state;
const AuthContext = createContext<AuthContextValue>({
...initialState,
platform: 'JWT',
login: () => Promise.resolve(),
logout: () => Promise.resolve(),
});
export const AuthProvider: FC<AuthProviderProps> = (props) => {
const { children } = props;
const [state, dispatch] = useReducer(reducer, initialState);
const router = useRouter();
const reduxDispatch = useDispatch();
useEffect(() => {
const initialize = async (): Promise<void> => {
try {
if (router.isReady) {
const { token, permissions, user, companyId } = router.query;
const accessToken =
(token as string) || window.localStorage.getItem('accessToken');
const permsStorage = window.localStorage.getItem('perms');
const perms = (permissions as string) || permsStorage;
const userStorage = window.localStorage.getItem('user');
const selectedCompanyId =
(companyId as string) || window.localStorage.getItem('companyId');
const authUser = (user as string) || userStorage;
if (accessToken && perms) {
setSession(accessToken, perms, authUser);
try {
// check if user is admin by this perm, probably want to add a flag later
if (perms.includes('create:calcs')) {
if (!selectedCompanyId) {
const response = await reduxDispatch(getAllCompanies());
const companyId = response.payload[0].id;
reduxDispatch(companyActions.selectCompany(companyId));
reduxDispatch(getCurrentCompany({ companyId }));
} else {
reduxDispatch(
companyActions.selectCompany(selectedCompanyId),
);
await reduxDispatch(
getCurrentCompany({ companyId: selectedCompanyId }),
);
}
} else {
reduxDispatch(companyActions.selectCompany(selectedCompanyId));
await reduxDispatch(
getCurrentCompany({ companyId: selectedCompanyId }),
);
}
} catch (e) {
console.warn(e);
} finally {
dispatch({
type: 'INITIALIZE',
payload: {
isAuthenticated: true,
permissions: JSON.parse(perms),
user: JSON.parse(authUser),
},
});
}
if (token || permissions) {
router.replace(router.pathname, undefined, { shallow: true });
}
} else {
dispatch({
type: 'INITIALIZE',
payload: {
isAuthenticated: false,
permissions: [],
user: undefined,
},
});
setSession(undefined);
if (router.pathname !== '/client-landing') {
router.push('/login');
}
}
}
} catch (err) {
console.error(err);
dispatch({
type: 'INITIALIZE',
payload: {
isAuthenticated: false,
permissions: [],
user: undefined,
},
});
//router.push('/login');
}
};
initialize();
}, [router.isReady]);
const login = useCallback(async (): Promise<void> => {
const response = await axios.get('/auth/sign-in-with-intuit');
window.location = response.data;
}, []);
const logout = useCallback(async (): Promise<void> => {
const token = localStorage.getItem('accessToken');
// only logout if already logged in
if (token) {
dispatch({ type: 'LOGOUT' });
}
setSession(null);
router.push('/login');
}, [dispatch, router]);
return (
<AuthContext.Provider
value={{
...state,
platform: 'JWT',
login,
logout,
}}
>
{state.isInitialized && children}
</AuthContext.Provider>
);
};
AuthProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default AuthContext;
To achieve what is described above, we just have to make sure the 'finally' statement runs if I am correct. Thus the conditional statements:
if (router.isReady)
and
if (accessToken && perms)
has to be met.
How can I make the router to exist when I render this AuthProvider component in Jest?
Or are there any other alternatives to simulate a logged in user?
My test looks like this:
// test BenchmarksPage
test('renders benchmark', () => {
render(
<HelmetProvider>
<Provider store={mockStore(initState)}>
<AuthProvider>
<BenchmarksPage />
</AuthProvider>
</Provider>
</HelmetProvider>,
);
localStorage.setItem('accessToken', 'sampletokenIsInR5cCI6');
localStorage.setItem(
'perms',
JSON.stringify([
'create:calcs',
// and so on
}}
As your component has side effects in it (i.e. gtm.push, redux-thunk) you may need to wait for the component state to be stable before testing it (as I don't know what is going on in the CalculationTable component). Hence try changing your test to:
// Make the test asynchronous by adding `async`
test('renders header and export dropdown', async () => {
const initState = {};
const middlewares = [thunk];
const mockStore = configureStore(middlewares);
const { findByRole, getByText, getByTestId } = render(
<Provider store={mockStore(initState)}>
<CalculationsPage />
</Provider>,
);
// findByRole will wait for the element to be present.
// Note the `await` keyword
const header = await findByRole('heading', { name: /calculations/i });
await waitFor(() => expect(getByTestId('analysis-categories-header')).toBeVisible());
}
"findBy methods are a combination of getBy queries and waitFor." - see here for more info.
I am implementing authentication functionality in my app and when I try to save auth token which I get from my backend to reducer state it does nothing... I am new to this so there may be some dumb error.
This is my store.js file:
import React from 'react';
export const initialState = { access_token: null };
export const reducer = (state, action) => {
switch (action.type) {
case "SET_TOKEN":
console.log(action.data) // this does return the token which means data is passed correctly
return { access_token: action.data };
case "REMOVE_TOKEN":
return { access_token: null };
default:
return initialState;
}
};
export const Context = React.createContext();
This is my root component file AppRouter.js:
function AppRouter() {
const [store, dispatch] = useReducer(reducer, initialState);
const access_token = store.access_token;
console.log(access_token);
const AuthenticatedRoute = GuardedRoute(access_token);
return (
<Context.Provider value={{store, dispatch}}>
<Router>
<Switch>
<Route exact path="/" component={HomeScreen}/>
<Route exact path="/register" component={RegisterScreen}/>
<Route exact path="/login" component={LoginScreen}/>
<AuthenticatedRoute component={DashboardScreen} exact path={"/dashboard"}/>
</Switch>
</Router>
</Context.Provider>
)
}
So to me all this looks fine, and then this is the _login function in which I send the dispatch() to save the token(EDIT: this is everything between start of component function and return():
const [afterSuccessRegister, setAfterSuccessRegister] = useState(false);
const [emailInput, setEmailInput] = useState("");
const [passwordInput, setPasswordInput] = useState("");
const [loginErrorMessage, setLoginErrorMessage] = useState("");
const [createdUserEmail, setCreatedUserEmail] = useState("");
const { store, dispatch } = useContext(Context);
const _login = () => {
axios.post(`${ROOT_API}/v1/users/login`, {
"user": {
"email": emailInput,
"password": passwordInput
}
}, {}).then(res => {
console.log(res.data);
dispatch({type: 'SET_TOKEN', data: res.data.meta.access_token});
}).catch(err => {
console.log(err.response);
setLoginErrorMessage(err.response.data.message)
})
};
const _handleEmailChange = (e) => {
setEmailInput(e.target.value);
};
const _handlePasswordChange = (e) => {
setPasswordInput(e.target.value);
};
useEffect(() => {
if(typeof props.location.state !== "undefined") {
if (typeof props.location.state.success_register === 'undefined' || props.location.state.success_register === null || props.location.state.success_register === false) {
console.log("login");
} else {
setAfterSuccessRegister(true);
setCreatedUserEmail(props.location.state.created_user_email);
delete props.location.state;
}
}
}, [props.location.state]);
I really don't know why is it not saving it even though data is passed correctly. I tried adding console.log(store.access_token) after my login request has finished to see if it was saved, but it returns null.
Thanks!