I don't know why it shows "{
userInfo: undefined
}", testing with postman making a post request was successful and I can get users info including the token but nothing shows on the redux devTools. Also when I try to login it says "Unsupported media type "application/x-www-form-urlencoded, application/json" in request." I don't know if it's related or not..
userReducers.js:
export const userLoginReducers = (state = {}, action) => {
switch (action.type) {
case USER_LOGIN_REQUEST:
return { laoding: true };
case USER_LOGIN_SUCCESS:
return { laoding: false, userInfo: action.payload };
case USER_LOGIN_FAIL:
return { laoding: false, error: action.payload };
case USER_LOGOUT:
return {};
default:
return state;
}
};
userActions.js:
export const login = (email, password) => async (dispatch) => {
try {
dispatch({
type: USER_LOGIN_REQUEST,
});
const config = {
headers: {
"Content-type": "application/json",
},
};
const { data } = await axios.post(
"/api/users/login/",
{ username: email, password: password },
config
);
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
} catch (error) {
dispatch({
type: USER_LOGIN_FAIL,
payload:
error.response && error.response.data.detail
? error.response.data.detail
: error.message,
});
}
};
Store.js:
import {
legacy_createStore as createStore,
combineReducers,
applyMiddleware,
} from "redux";
//import { configureStore } from "#reduxjs/toolkit";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import {
productListReducers,
productDetailsReducers,
} from "./reducers/ProductReducers";
import { cartReducer } from "./reducers/CartReducers";
import { userLoginReducers } from "./reducers/UserReducers";
const reducer = combineReducers({
productList: productListReducers,
productDetails: productDetailsReducers,
cart: cartReducer,
userLogin: userLoginReducers,
});
const cartItemsFromStorage = localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [];
const userInfoFromStorage = localStorage.getItem("userInfo")
? JSON.parse(localStorage.getItem("userInfo"))
: null;
const initialState = {
cart: { cartItems: cartItemsFromStorage },
userLogin: { userInfo: userInfoFromStorage },
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
LoginScreen.js:
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { Row, Col, Button, Form } from "react-bootstrap";
//import products from "../../products";
import Message from "../Message";
import Loader from "../Loader";
import { useNavigate, useLocation, useSearchParams } from "react-router-dom";
import { login } from "../../actions/UserActions";
import LoginForm from "../LoginForm";
function LoginScreen() {
const [searchParams, setSearchParams] = useSearchParams();
const { search } = useLocation();
const navigate = useNavigate();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const dispatch = useDispatch();
const redirect = searchParams.get(search.split("=")) || 1;
const userLogin = useSelector((state) => state.userLogin);
const { error, loading, userInfo } = userLogin;
useEffect(() => {
if (userInfo) {
navigate(redirect);
setSearchParams(searchParams);
}
}, [navigate, userInfo, redirect, searchParams, setSearchParams]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(login(email, password));
};
return (
<LoginForm>
<h1>Sign In</h1>
{error && <Message variant="danger">{error}</Message>}
{loading && <Loader />}
<Form onSubmit={submitHandler}>
<Form.Group>
<Form.Label>Email Address</Form.Label>
<Form.Control
type="email"
placeholder="Enter Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Enter Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
></Form.Control>
</Form.Group>
<Button type="submit" className="mt-3" variant="primary">
Sign In
</Button>
</Form>
<Row className="py-3">
<Col>
New Customer?
<Link to={redirect ? `/register?redirect=${redirect}` : "/register"}>
Register
</Link>
</Col>
</Row>
</LoginForm>
);
}
export default LoginScreen;
Related
I dont understand why does the below error keeps happening, so I could really use some help!
Error:
Login.jsx:10 Uncaught TypeError: Cannot destructure property 'user' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is undefined.
at Login (Login.jsx:10:1)
Context.js:
import { createContext, useEffect, useReducer } from "react";
import Reducer from "./Reducer";
const INITIAL_STATE = {
user: null,
isFetching: false,
error: false,
};
export const Context = createContext(INITIAL_STATE);
export const ContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(Reducer, INITIAL_STATE);
return (
<Context.Provider
value = {{
user: state.user,
isFetching: state.isFetching,
error: state.error,
dispatch,
}}
>
{children}
</Context.Provider>
);
};
Login.js:
import axios from 'axios';
import { useContext, useRef } from 'react';
import { Link } from 'react-router-dom';
import { Context } from '../../context/Context';
import './login.css';
export default function Login() {
const userRef = useRef();
const passwordRef = useRef();
const { user, dispatch, isFetching } = useContext(Context); //error
const handleSubmit = async (e) => {
e.preventDefault();
dispatch({ type: 'LOGIN_START' });
try {
const res = await axios.post(`http://localhost:5000/api/auth/login`, {
username: userRef.current.value,
password: passwordRef.current.value,
});
dispatch({ type: 'LOGIN_SUCCESS', payload: res.data });
} catch (err) {
dispatch({ type: 'LOGIN_FAILURE' });
console.log(err);
}
};
console.log(user);
return (
<div className='login'>
<span className='loginTitle'>Login</span>
<form className='loginForm' onSubmit={handleSubmit}>
<label>Username</label>
<input
type='text'
className='loginInput'
placeholder='Enter your username...'
ref={userRef}
/>
<label>Password</label>
<input
type='password'
className='loginInput'
placeholder='Enter your password...'
ref={passwordRef}
/>
<button className='loginButton' type='submit' disabled={isFetching}>
Login
</button>
</form>
<button className='loginRegisterButton'>
<Link className='link' to='/register'>
Register
</Link>
</button>
</div>
);
}
Did you wrap the parent of the Login component with the ContextProvider?
import { ContextProvider } from '../../context/Context';
import Login from "..."
function App() {
return(
<>
<ContextProvider>
<Login>
//any other child over here can access the context from this provider
</ContextProvider>
</>
)
}
export default App;
I try to get user information by using redux but the request fails with error 500 and the network response returns this kind of error message "{"err": "E11000 duplicate key error collection: private.users index: email_1 dup key: { email: "samarkan#mail.com" }"}".I'am very confused friends,please can someone help me to fix it there's all my code:
userReducer.js:
import Axios from "axios";
export const register = ({name, fname, phone, email, password}) => async (dispatch)=> {
dispatch({type: "USER_REGISTER_REQUEST", payload: {name, fname, phone, email}});
try {
const {data} = await Axios.post("api/users/register",{name, fname, phone, email})
dispatch({type: "USER_REGISTER_SUCCESS", payload: data })
} catch (error) {
dispatch({type: "USER_REGISTER_FAIL", payload: error.response && error.response.data.message ? error.response.data.message : error.message})
}
};
export const registerReducer = (state = {loading: true, userinfo: {}}, action) => {
switch(action.type) {
case "USER_REGISTER_REQUEST":
return {loading: true}
case "USER_REGISTER_SUCCESS":
return {loading: false, userInfo: action.payload};
case "USER_REGISTER_FAIL":
return {loading: false, error: action.payload}
default:
return state;
}
};
registerPage:
import React from "react";
import {useDispatch, useSelector} from "react-redux";
import InputGroup from "./common/InputGroup";
import {useState, useEffect} from "react";
import {register} from "../reducers/userReducer";
import {useNavigate, useParams} from "react-router-dom";
const RegisterPage = () => {
const dispatch = useDispatch();
const [name, setName] = useState('');
const [fname, setFname] = useState('');
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirm, setConfirm] = useState('');
const navigate = useNavigate();
const validateEmail = (email) => {
return String(email)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
);
};
const submitHandler = (e) => {
if(password === confirm && validateEmail(email)) {
dispatch(register({name, fname, phone, email})).then((user)=> navigate({pathname: "/profile", search: `?id=${user._id}&name=${user.name}&fname=${user.fname}&email=${user.email}`}) );
} else {
window.alert("password and confirm password not the same try again or email not conform!")
}
}
return (
<div>
<h2>Welcome to private <span>Let's be private</span></h2>
<p id="icon" >Pr</p>
<div className="form">
<InputGroup value={name} type="text" label="name" name="name" onChange={(e)=> setName(e.target.value)} />
<InputGroup value={fname} type="text" label="fname" name="fname" onChange={(e)=> setFname(e.target.value)}/>
<InputGroup value={phone} type="phone" label="phone" name="phone" onChange={(e)=> setPhone(e.target.value)}/>
<InputGroup value={email} type="email" label="email" name="email" onChange={(e)=> setEmail(e.target.value)}/>
<InputGroup value={password} type="password" label="password" name="password" onChange={(e)=> setPassword(e.target.value)} />
<InputGroup value={confirm} type="password" label="confirm password" name="confirm" onChange={(e)=> setConfirm(e.target.value)} />
<button onClick={(e)=>submitHandler()} >Register</button>
</div>
</div>
)
};
export default RegisterPage;
````
````
store.js:
import { createStore } from "redux";
import {compose, applyMiddleware} from "redux";
import {combineReducers} from "redux";
import thunk from "redux-thunk";
import {registerReducer} from "./reducers/userReducer";
const initialState = {user: {}};
const reducer = combineReducers({
user: registerReducer
});
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore( reducer, initialState, composeEnhancer(applyMiddleware(thunk)))
export default store;
````
[![enter image description here][1]][1]
[1]: https://i.stack.imgur.com/a3kBH.png
Okay I'm trying to use global states to store if an user is logged in or not. To do so, I've created a Context file as follows:
import { createContext } from "react";
export const LoginContext= createContext({})
I also have my App.jsx:
import React, { useState } from 'react';
import Component1 from './Component1.jsx';
import Component2 from './Component2.jsx';
import Component3 from './Component3.jsx';
import { LoginContext } from '../Helper/Context.js';
function App(){
const [loggedIn, setLoggedIn] = useState(false);
return (
<LoginContext.Provider value={{loggedIn, setLoggedIn}}>
<Component1 />
<Component2 />
<Component3 />
</LoginContext.Provider>
)
}
export default App;
And then I have my Login component:
import React, {useState, useContext} from "react";
import Axios from 'axios';
import { Link, useHistory } from 'react-router-dom';
import { LoginContext } from "../Helper/Context";
import NavbarHome from "./NavbarHome";
function Login()
{
Axios.defaults.withCredentials = true;
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [loginStatus, setLoginStatus] = useState(false);
const {loggedIn, setLoggedIn} = useContext(LoginContext);
const [error, setErrorStatus] = useState("");
let history = useHistory();
let user = {
id: null,
nombre: null,
email: null
};
const login = () => {
Axios.post('http://localhost:3001/login', {
email: email,
password: password,
}).then((response)=>{
if(!response.data.auth) {
setLoginStatus(false);
setErrorStatus(response.data.message)
setLoggedIn(false);
}
else{
localStorage.setItem("token", response.data.token)
user = {
id: response.data.result[0].id,
name: response.data.result[0].name,
email: response.data.result[0].email
}
setLoginStatus(true);
setLoggedIn(true);
history.push('/perfil');
}
});
};
const userAuthenticated = () => {
Axios.get("http://localhost:3001/isUserAuth", {
headers: {
"x-access-token": localStorage.getItem("token"),
},
}).then((response) => {
console.log(response);
});
}
return(
<div>
<div>
<NavbarHome />
<div>
<div>
<h1>Login</h1>
<p className="label-login">Email:</p>
<input type="text" placeholder="Email..." onChange={(event) => {setEmail(event.target.value);}}/>
<p className="label-login">Contraseña:</p>
<input type="password" placeholder="Password..." onChange={(event) => {setPassword(event.target.value);}}/> <br />
<button onClick={login}>Login</button>
<p style={{marginTop: '1.3rem', color: 'red'}}>{error}</p>
<p><Link to='/registro'>Register here!</Link></p>
</div>
</div>
</div>
</div>
);
}
export default Login;
The output that I receive is this:
It complains about the line where I do this: setLoggedIn(false); or setLoggedIn(true); I'd like to use that state instead of setLoginStatus (which is the one I'm currently using)
Any ideas on how to fix it?
For some reason my Login component is not redirecting a successful login to the right page. I am using a Spring boot backend with a React frontend and am pretty sure I can do this with react on the frontend by using history.push('/profile') to allow a successful login to be redirected to the /profile page but for some reason it stays on /login even after successfully logging in. Any ideas what I am doing wrong? Thank you!
Login.jsx:
import React, {useState, useEffect} from 'react';
import {useDispatch, useSelector} from 'react-redux';
import {Link} from 'react-router-dom';
import {Formik, Field, Form, ErrorMessage} from 'formik';
import * as Yup from 'yup';
import {login} from '../../slices/auth';
import {clearMessage} from '../../slices/messages';
const Login = (props) => {
const [loading, setLoading] = useState(false);
const {isLoggedIn} = useSelector((state) => state.auth);
const {message} = useSelector((state) => state.message);
const dispatch = useDispatch();
useEffect(() => {
dispatch(clearMessage());
}, [dispatch]);
const initialValues = {
username: '',
password: '',
};
const validationSchema = Yup.object().shape({
username: Yup.string().required('Please enter your username'),
password: Yup.string().required('Please enter your password'),
});
const handleLogin = (formValue) => {
const {username, password} = formValue;
setLoading(true);
dispatch(login({username, password}))
.unwrap()
.then(() => {
props.history.push('/profile');
window.location.reload();
})
.catch(() => {
setLoading(false);
});
};
if (isLoggedIn) {
return <Link to='/profile' />;
}
return (
<div className='login'>
<div className='card card-container'>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleLogin}
>
<Form>
<div className='form-group'>
<Field
name='username'
type='text'
className='form-control'
placeholder='Username'
/>
<ErrorMessage
name='username'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<Field
name='password'
type='password'
className='form-control'
placeholder='Password'
/>
<ErrorMessage
name='password'
component='div'
className='alert alert-danger'
/>
</div>
<div className='form-group'>
<button
type='submit'
className='btn btn-primary btn-block'
disabled={loading}
>
{loading && (
<span className='spinner-border spinner-border-sm'></span>
)}
<span>Login</span>
</button>
</div>
</Form>
</Formik>
</div>
{message && (
<div className='form-group'>
<div className='alert alert-danger' role='alert'>
{message}
</div>
</div>
)}
</div>
);
};
export default Login;
auth.js:
// We’re gonna import AuthService to make asynchronous HTTP requests with trigger one or more dispatch in the result.
// – register(): calls the AuthService.register(username, email, password) & dispatch setMessage if successful/failed
// – login(): calls the AuthService.login(username, password) & dispatch setMessage if successful/failed
// – logout(): calls the AuthService.logout().
// setMessage is imported from message slice that we’ve created above.
// We also need to use Redux Toolkit createAsyncThunk which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise.
//There are 3 async Thunks to be exported:
// register
// login
// logout
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
messages.js:
// This updates message state when message action is dispatched from anywhere in the application. It exports 2 action creators:
// setMessage
// clearMessage
import {createSlice} from '#reduxjs/toolkit';
const initialState = {};
const messageSlice = createSlice({
name: 'message',
initialState,
reducers: {
setMessage: (state, action) => {
return {message: action.payload};
},
clearMessage: () => {
return {message: ''};
},
},
});
const {reducer, actions} = messageSlice;
export const {setMessage, clearMessage} = actions;
export default reducer;
Remove the window.location.reload(); from the handleLogin function, it is reloading your app and killing the navigation action.
Since it appears you are using react-router-dom v6, there are no route props and there is no history object. Instead, there is a useNavigate hook.
import { useNavigate } from 'react-router-dom';
...
const Login = (props) => {
const navigate = useNavigate();
...
const handleLogin = (formValue) => {
const { username, password } = formValue;
setLoading(true);
dispatch(login({ username, password }))
.unwrap()
.then(() => {
navigate('/profile', { replace: true });
})
.catch(() => {
setLoading(false);
});
};
...
Hi I am new to redux and authentication. I am creating a react/redux app where a user can login and be able to add a story by submitting a form. I am able to login but when I get to the story creation page, I click submit and I get a POST http://localhost:3000/api/v1/stories 401 (Unauthorized) error.
I am logging in using an API that gives a token on login. I then save the username and token to sessionstorage. But how would I fix this error?
App.js
import './App.scss';
import Login from './components/Login';
import { Router, Switch, Route, NavLink } from 'react-router-dom';
import PrivateRoute from './utils/PrivateRoute';
import CreateStory from './components/CreateStory';
import history from './utils/history';
function App() {
return (
<div className="App">
<Router history={history}>
<Switch>
<Route exact path="/" component={Login} />
<PrivateRoute path="/user" component={CreateStory}/>
</Switch>
</Router>
</div>
);
}
export default App;
PrivateRoute.js
import { useSelector } from 'react-redux'
// handle the private routes
function PrivateRoute({ component: Component, ...rest }) {
const getToken = useSelector((state)=> state.loginReducer.token)
console.log(getToken)
return (
<Route
{...rest}
render={(props) => getToken ? <Component {...props} /> : <Redirect to={{ pathname: '/', state: { from: props.location } }} />}
/>
)
}
export default PrivateRoute;
CreateStory.js
import React, { useState } from 'react'
import { createStory } from '../redux/actions'
import { useDispatch } from "react-redux";
const CreateStory = () => {
const [summary, setSummary] = useState("");
const [description, setDescription] = useState("");
const [type, setType] = useState("");
const [complexity, setcomplexity] = useState("");
const usedispatch = useDispatch();
const userCreateStory = (summary, description, type, complexity) => usedispatch(createStory({
'summary': summary,
'description': description,
'type': type,
'complexity': complexity
}));
const handleSummaryChange = e => {
setSummary(e.target.value)
}
const handleDescriptionChange = e => {
setDescription(e.target.value)
}
const handleTypeChange = e => {
setType(e.target.value)
}
const handleComplexityChange = e => {
setcomplexity(e.target.value)
}
const handleSubmit = e => {
e.preventDefault();
userCreateStory('a','b','c','d')
// setTimeout(()=> history.push("/user"), 1000 );
}
return (
<div>
<form className='create-story-form'>
<label for="summary">Summary:</label>
<input name="summary" type='text' onChange={handleSummaryChange}/>
<label for="desc">Description:</label>
<textarea name="desc" type='text' onChange={handleDescriptionChange}/>
<label for="type">Type:</label>
<select name="type">
<option value="enhancement">Enchancement</option>
<option value="bugfix">Bugfix</option>
<option value="development">Development</option>
<option value="qa">QA</option>
</select>
<label for="complexity">Complexity:</label>
<select name="complexity">
<option value="Low">Low</option>
<option value="Mid">Mid</option>
<option value="High">High</option>
</select>
<label for="time">Estimated time for completion:</label>
<input name="time" type='text' />
<label for="cost">Cost:</label>
<input name="cost" type='number' />
<button onClick={handleSubmit}>Submit</button>
</form>
</div>
)
}
export default CreateStory;
Login.js
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { login, roleChange } from '../redux/actions' //OUR ACTIONS
import { useSelector } from 'react-redux'
import history from '../utils/history';
import { withRouter } from 'react-router-dom';
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const usedispatch = useDispatch();
const userLogin = (email, password) => usedispatch(login({'email': email, 'password': password }));
const switchToAdmin = () => usedispatch(roleChange('admin'));
const switchToUser = () => usedispatch(roleChange('user'));
const currentRole = useSelector((state)=> state.loginReducer.role)
const handleRoleChange = e => {
e.preventDefault();
if(currentRole === 'user')
switchToAdmin();
else if(currentRole === 'admin' )
switchToUser()
}
const handleEmailChange = e => {
setEmail(e.target.value)
}
const handlePasswordChange = e => {
setPassword(e.target.value)
}
const handleSubmit = e => {
e.preventDefault();
userLogin(email, password)
setTimeout(()=> history.push("/user"), 1000 );
}
const disabled = () => {
return email === "" || password === ""
}
return (
<div>
<form className='login-form'>
<input type='email' name='email' placeholder='Email' onChange={handleEmailChange}/>
<input type='password' name='password' placeholder='Password' onChange={handlePasswordChange}/>
<button type='submit' disabled={disabled()} onClick={handleSubmit}>Login</button>
</form>
<button onClick={handleRoleChange}>Switch to {currentRole === 'user' ? 'admin' : 'user'}</button>
</div>
)
}
export default withRouter(Login);
actionTypes.js
export const SET_LOGIN_STATE = "SET_LOGIN_STATE"
export const SET_ROLE_STATE = "SET_ROLE_STATE"
export const CREATE_STORY = "CREATE_STORY"
initialState.js:
import { getToken } from '../utils/Common'
export const initialState = {
isLoggedIn: false,
userId: '',
role: 'user',
token: getToken,
data: '',
};
reducers.js
import { initialState } from './initialState';
import * as t from './actionTypes';
export const loginReducer = (state = initialState, action) => {
switch (action.type) {
case t.SET_ROLE_STATE:
return {
...state,
role: action.payload,
};
case t.SET_LOGIN_STATE:
return {
...state,
...action.payload, // this is what we expect to get back from API call and login page input
isLoggedIn: true, // we set this as true on login
};
default:
return state;
}
};
export const storyReducer = (state = initialState, action) => {
switch (action.type) {
case t.CREATE_STORY:
return {
...state,
role: action.payload,
};
default:
return state;
}
}
actions.js:
import * as t from './actionTypes';
import { setUserSession } from '../utils/Common';
// this is what our action should look like which dispatches the "payload" to reducer
const setLoginState = (loginData) => {
return {
type: t.SET_LOGIN_STATE,
payload: loginData, //{ ...json, userId: email }
};
};
const setStoryState = (storyData) => {
return {
type: t.CREATE_STORY,
payload: storyData,
};
};
export const login = (loginInput) => { //our login action
const { email, password } = loginInput;
return (dispatch) => { // don't forget to use dispatch here!
return fetch('http://localhost:3000/api/v1/signin', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(loginInput),
})
.then((response) => response.json()) //json will be the response body
.then((json) => {
// if (json.msg === 'success') { // response success checking logic could differ
// console.log(json)
dispatch(setLoginState({ ...json, userId: email })); // our action is called here with object as parameter, this is our payload
//we appended json object to our state
// } else {
// alert('Login Failed', 'Email or Password is incorrect');
// }
setUserSession(json.token, json.lastName)
})
.catch((err) => {
alert('Login Failed', 'Some error occured, please retry');
console.log(err);
});
};
};
export const roleChange = role => {
return {
type: t.SET_ROLE_STATE,
payload: role
};
}
/**
* story input:
{
"summary": "string",
"description": "string",
"type": "string",
"complexity": "string"
}
*/
export const createStory = storyInput => {
const { summary, description, type, complexity } = storyInput;
return (dispatch) => { // don't forget to use dispatch here!
return fetch('http://localhost:3000/api/v1/stories', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(storyInput),
})
.then((response) => response.json()) //json will be the response body
.then((json) => {
// if (json.msg === 'success') { // response success checking logic could differ
console.log(json)
// dispatch(setStoryState({ // our action is called here with object as parameter, this is our payload
// summary: summary,
// description: description,
// type: type,
// complexity: complexity
// })); // our action is called here
// } else {
// alert('Login Failed', 'Email or Password is incorrect');
// }
})
.catch((err) => {
alert('Some error occured, please retry');
console.log(err);
});
};
}
Common.js
// return the user data from the session storage
export const getUser = () => {
const userStr = sessionStorage.getItem('user');
if (userStr) return JSON.parse(userStr);
else return null;
}
// return the token from the session storage
export const getToken = () => {
return sessionStorage.getItem('token') || null;
}
// remove the token and user from the session storage
export const removeUserSession = () => {
sessionStorage.removeItem('token');
sessionStorage.removeItem('user');
}
// set the token and user from the session storage
export const setUserSession = (token, user) => {
sessionStorage.setItem('token', token);
sessionStorage.setItem('user', JSON.stringify(user));
}
You'll have to pass the auth token from the sessionStorage to the header of API you are posting your story to :-
const token = sessionStorage.getItem('token'); //Add this line
return fetch('http://localhost:3000/api/v1/stories', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Authorization: `Bearer ${token}` //Add this line
},
body: JSON.stringify(storyInput),
})