I'm trying to fetch appointment data from my rails-api backend using the createAsyncThunk function. When I
console.log('appointmentDate', appointmentDate);
console.log('doctorId', doctorId);
console.log('userId', userId);
After clicking the submit button to create a new appointment; I get:
appointmentDate => 2021-07-30 which is fine
doctorId => 2 which is also fine.
userId => undefined which is not what I'm expecting. I expected a number just like doctorId
I have tested the backend with postman and everything is fine. And since I can't fetch the correct data. I can't create an appointment when I submit the form
This is how I'm destructuring the user state before adding it to the postAppointment action creator for dispatch
const { data: userData } = useSelector((state) => state.user);
const { userId } = userData;
since `user_Id` is part of the `user` state in my `store`. I can destructure it as above and add it to my `dispatch`.
And this is how I'm dispatching it inside the NewAppointment component
dispatch(postAppointments({ userId, doctorId, appointmentDate }))
.then(() => {
setSuccessful(true);
alert.show('Appointment created', {
type: 'success',
timeout: 2000,
});
setLoading(false);
})
.catch((error) => {
console.log(error.message);
setSuccessful(false);
});
I don't know what I'm missing with the user destructuring and dispatching the action creator?
Here are the other codes
src/redux/appointmentsSlice
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import API from '../api/api';
export const postAppointments = createAsyncThunk(
'appointments/postAppointments',
async (
{
userId, appointmentDate, doctorId,
},
) => {
console.log('appointmentDate', appointmentDate);
console.log('doctorId', doctorId);
console.log('userId', userId);
const response = await fetch(`${API}/users/${userId}/appointments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
appointmentDate,
doctorId,
userId,
}),
});
const data = await response.json();
console.log('appointmentsData', data);
if (!response.ok) throw new Error(data.failure);
localStorage.setItem('token', data.jwt);
console.log('localstorageData', data);
return data;
},
);
export const appointmentsSlice = createSlice({
name: 'appointments',
initialState: {
loading: false,
error: null,
data: [],
},
extraReducers: {
[postAppointments.pending]: (state) => {
state.loading = true;
},
[postAppointments.rejected]: (state, action) => {
state.loading = false;
state.error = action.error.message;
},
[postAppointments.fulfilled]: (state, action) => {
state.loading = false;
state.data = action.payload;
},
},
});
export default appointmentsSlice.reducer;
src/components/NewAppointment
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Redirect } from 'react-router-dom';
import { postAppointments } from '../redux/appointmentsSlice';
import { getDoctors } from '../redux/doctorsSlice';
const NewAppointment = () => {
const [appointmentDate, setAppointmentDate] = useState('');
const [doctorId, setDoctorId] = useState('');
const [successful, setSuccessful] = useState(false);
const [loading, setLoading] = useState(false);
const { data: userData } = useSelector((state) => state.user);
const { userId } = userData;
console.log('userData', userData);
const dispatch = useDispatch();
const { data, error } = useSelector((state) => state.doctors);
console.log('data', data);
useEffect(() => {
if (data === null && userData) {
dispatch(getDoctors())
.then(() => {
loading(false);
})
.catch(() => {
// setError('Unable to get doctors list');
});
}
}, [data, dispatch]);
const onChangeDoctorId = (e) => {
const doctorId = e.target.value;
setDoctorId(doctorId);
console.log('doctorUnchange', doctorId);
};
const onChangeAppointmentDate = (e) => {
const appointmentDate = e.target.value;
setAppointmentDate(appointmentDate);
console.log('apptntmentonchange', appointmentDate);
};
const handleBooking = (e) => {
e.preventDefault();
setSuccessful(false);
// eslint-disable-next-line no-underscore-dangle
dispatch(postAppointments({ userId, doctorId, appointmentDate }))
.then(() => {
setSuccessful(true);
alert.show('Appointment created', {
type: 'success',
timeout: 2000,
});
setLoading(false);
})
.catch((error) => {
console.log(error.message);
setSuccessful(false);
});
};
console.log('data now', data);
const options = data && (
data.map((doctor) => (
<option
key={doctor.id}
value={doctor.id}
>
{doctor.name}
</option>
))
);
if (!userData) {
return <Redirect to="/login" />;
}
if (successful) {
return <Redirect to="/appointments" />;
}
return (
<div className="col-md-12">
<div className="card card-container">
<form onSubmit={handleBooking}>
{ !successful && (
<div>
<div className="form-group create">
<label htmlFor="appointmentDate" className="control-label">
Appointment Date
<input
type="date"
className="form-control"
name="appointmentDate"
id="appointmentDate"
required
value={appointmentDate}
onChange={onChangeAppointmentDate}
/>
</label>
</div>
<div className="form-group create">
<label htmlFor="doctorId">
Select from list:
<select className="form-control" id="doctorId" onChange={onChangeDoctorId} value={doctorId}>
{loading ? <option>Loading..</option> : options }
</select>
</label>
</div>
<div className="form-group create">
<button className="btn btn-primary btn-block" disabled={loading} type="submit">
{loading && (
<span className="spinner-border spinner-border-sm" />
)}
<span>Book</span>
</button>
</div>
</div>
)}
{error && (
<div className="form-group">
<div className={successful ? 'alert alert-success' : 'alert alert-danger'} role="alert">
{error}
</div>
</div>
)}
</form>
</div>
</div>
);
};
export default NewAppointment;
And the API url returns undefined for the userId variable as shown below
POST https://agile-escarpment-87534.herokuapp.com/api/v1/users/undefined/appointments 404 (Not Found)
This is my first project using createAsyncThunk and still trying to understand how it works. I have also checked similar posts, but none solved my issue.
Any support or constructive criticism is welcome.
you will need the token from here
const { user_id, jwt } = userData;
add the extracted token from the userData to postAppointments
dispatch(postAppointments({ user_id, doctor_id, appointment_date, jwt }))
.then(() => {
setSuccessful(true);
alert.show('Appointment created', {
type: 'success',
timeout: 2000,
});
setLoading(false);
})
.catch((error) => {
console.log(error.message);
setSuccessful(false);
});
finally add the token to the fetch request
export const postAppointments = createAsyncThunk(
'appointments/postAppointments',
async (
{
user_id, appointment_date, doctor_id, jwt
},
) => {
console.log('appointmentDate', appointment_date);
console.log('doctor_id', doctor_id);
console.log('user_id', user_id);
const response = await fetch(`${API}/appointments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
// this your missing header
Authorization: `Bearer ${jwt}`
},
body: JSON.stringify({
appointment_date,
doctor_id,
user_id,
}),
});
const data = await response.json();
console.log('appointmentsData', data);
if (!response.ok) throw new Error(data.failure);
localStorage.setItem('token', data.jwt);
console.log('localstorageData', data);
return data;
},
);
This is just a guess.. because I don't have actual code.
UPDATE:
Another way to write that fetch:
fetch(`${API}/appointments`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${jwt}`
},
body: JSON.stringify({
appointment_date,
doctor_id,
user_id,
}),
}).then(response => {
response.json();
}).then(data => {
console.log('Success:', data);
localStorage.setItem('token', data.jwt);
}).catch((error) => {
// request just fail
throw new Error('Error:', error);
});
inspired by: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
Related
It seemed like something that'd be straightforward to accomplish, but I'm kind of struggling with this. The current behavior with the code below correctly increments likes/dislikes on each photo upon clicking any specific photo, but not the like/dislike state upon clicking which's what needs to be done to toggle between the Like and Dislike buttons.
Just like the functionality as Instagram - How can I make it so that the green like button (success) is displayed when the respective photo isn't liked.
And similarly, display the red dislike button (danger) when the respective photo is liked?
Below I tried to only use the setUserClickedLike() hook in both handleLike() and handleDislike() but it doesn't work.
const Grid = () => {
let authToken = localStorage.getItem("token");
const [gridData, setGridData] = useState([]);
// User clicks likes
const [userLikedPhotos, setUserLikedPhotos] = useState({});
const [userClickedLike, setUserClickedLike] = useState(false);
const [userClickedDislike, setUserClickedDisliked] = useState(false);
useEffect(() => {
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
axios
.get("http://127.0.0.1:8000/api/get-user-uploads-data", { headers })
.then((resp) => {
console.log(resp.data);
setGridData(resp.data);
})
.catch((err) => {
console.log(err);
});
}, []);
const handleLike = (likedPhotoUserId, userName, likedPhotoId) => {
const url = "http://127.0.0.1:8000/api/like";
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
let data = {
UserID: likedPhotoUserId,
userName: userName,
likedPhotoId: likedPhotoId,
};
userLikedPhotos[likedPhotoUserId] = true;
gridData.find(function (photo) {
setUserClickedLike(photo.UserID === likedPhotoUserId);
return photo.UserID === likedPhotoUserId;
}).likes++;
axios
.post(url, data, { headers })
.then((resp) => {
setDataFromUserLikesTable(resp.data);
})
.catch((err) => {
console.log(err);
});
setUserLikedPhotos({ ...userLikedPhotos });
};
const handleDislike = (likedPhotoUserId, userName, likedPhotoId) => {
const url = "http://127.0.0.1:8000/api/dislike";
const headers = {
Accept: "application/json",
Authorization: `Bearer ${authToken}`,
};
let data = {
UserID: likedPhotoUserId,
userName: userName,
dislikedPhotoId: likedPhotoId,
};
// dislike
delete userLikedPhotos[likedPhotoUserId];
gridData.find(function (photo) {
setUserClickedLike(photo.UserID === likedPhotoUserId);
return photo.UserID === likedPhotoUserId;
}).likes--;
axios
.post(url, data, { headers })
.then((resp) => {
console.log(resp.data);
})
.catch((err) => {
console.log(err);
});
setUserLikedPhotos({ ...userLikedPhotos });
};
return (
<>
<div className="container">
<div className="img-container">
{gridData.map((photos, index) => {
return (
<>
<div className="userDetails">
<span className="likesAmt">❤️ {photos.likes}</span>
<br />
{!photos.is_liked && !userClickedLike ? (
<Button
variant="success"
onClick={() =>
handleLike(
photos.UserID,
photos.name,
photos.photo_id,
photos.is_liked
)
}
>
Like
</Button>
) : (
<Button
variant="danger"
onClick={() =>
handleDislike(
photos.UserID,
photos.name,
photos.photo_id,
photos.is_liked
)
}
>
Dislike
</Button>
)}
</div>
</>
);
})}
</div>
</div>
</>
);
};
export default Grid;
I am trying to update user information (name, email, password) but getting two errors.
If I try to enter & update name or Email (DATA DOES NOT GET UPDATED) I get 404 error :
Request:
Response:
But, if I try to enter and update Password it gets UPDATED (as I have to enter new password while logging in again) but it shows these error's immediately after submit button:
I have tried updating user data by putting data directly through thunder client & it's getting updated:
Here is my source code:
FRONTEND
ProfileScreen.js
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import ErrorMessage from "../../components/ErrorMessage/ErrorMessage";
import Message from "../../components/Message/Message";
import Loader from "../../components/Loader/Loader";
import {
getUserDetails,
updateUserProfile,
} from "../../redux/actions/userActions";
import "./ProfileScreen.scss";
import { USER_UPDATE_PROFILE_RESET } from "../../redux/constants/userConstants";
const ProfileScreen = ({ location, history }) => {
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const [message, setMessage] = useState(null);
const regex =
/^(?=.*[A-Za-z])(?=.*\d)(?=.*[#$!%*#?&])[A-Za-z\d#$!%*#?&]{8,}$/;
const dispatch = useDispatch();
const userDetails = useSelector((state) => state.userDetails);
const { loading, error, user } = userDetails;
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const userUpdateProfile = useSelector((state) => state.userUpdateProfile);
const { success } = userUpdateProfile;
useEffect(() => {
if (!userInfo) {
history.push("/login");
} else {
if (!user.name || !user || success) {
dispatch({ type: USER_UPDATE_PROFILE_RESET });
dispatch(getUserDetails("profile"));
} else {
setName(user.name);
setEmail(user.email);
}
}
}, [history, userInfo, dispatch, user, success]);
const passwordHandler = (e) => {
e.preventDefault();
!regex.test(password)
? setMessage(
"Password must contain atleast 8 characters & one alphabet, number & special character"
)
: password !== confirmPassword
? setMessage("Passwords do not match!")
: dispatch(updateUserProfile({ id: user._id, password }));
};
const enameHandler = (e) => {
e.preventDefault();
dispatch(updateUserProfile({ id: user._id, name, email, password }));
};
return (
<>
<div className="profile-container">
<div className="profile">
{message && <ErrorMessage error={message} />}
{error && <ErrorMessage error={error} />}
{success && <Message success={"Profile Updated"} />}
<div className="profile-form">
<h2>User Profile</h2>
{loading ? (
<Loader />
) : (
<div>
<form onSubmit={enameHandler}>
<div className="profile-form-items">
<h3> Update Name or Email</h3>
<input
className="profile-input"
type="name"
placeholder="New Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
className="profile-input"
type="email"
placeholder="New Email address"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit" value="submit">
Update
</button>
</div>
</form>
<form onSubmit={passwordHandler}>
<div className="profile-form-items">
<h3>Update Password</h3>
<input
className="profile-input"
type="password"
placeholder="New Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<input
className="profile-input"
type="password"
placeholder="Confirm New Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
/>
<button type="submit" value="submit">
Update
</button>
</div>
</form>
</div>
)}
</div>
</div>
</>
);
};
export default ProfileScreen;
userAction.js
import axios from "axios";
import {
USER_DETAILS_FAIL,
USER_DETAILS_REQUEST,
USER_DETAILS_SUCCESS,
USER_LOGIN_FAIL,
USER_LOGIN_REQUEST,
USER_LOGIN_SUCCESS,
USER_LOGOUT,
USER_REGISTER_FAIL,
USER_REGISTER_REQUEST,
USER_REGISTER_SUCCESS,
USER_UPDATE_PROFILE_FAIL,
USER_UPDATE_PROFILE_REQUEST,
USER_UPDATE_PROFILE_SUCCESS,
} from "../constants/userConstants";
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",
{ email, 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.message
? error.response.data.message
: error.message,
});
}
};
export const logout = () => (dispatch) => {
localStorage.removeItem("userInfo");
dispatch({
type: USER_LOGOUT,
});
};
export const register = (name, email, password) => async (dispatch) => {
try {
dispatch({
type: USER_REGISTER_REQUEST,
});
const config = {
headers: {
"Content-Type": "application/json",
},
};
const { data } = await axios.post(
"/api/users",
{ name, email, password },
config
);
dispatch({
type: USER_REGISTER_SUCCESS,
payload: data,
});
dispatch({
type: USER_LOGIN_SUCCESS,
payload: data,
});
localStorage.setItem("userInfo", JSON.stringify(data));
} catch (error) {
dispatch({
type: USER_REGISTER_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const getUserDetails = (id) => async (dispatch, getState) => {
try {
dispatch({
type: USER_DETAILS_REQUEST,
});
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.get(`/api/users/${id}`, config);
dispatch({
type: USER_DETAILS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: USER_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const updateUserProfile = (user) => async (dispatch, getState) => {
try {
dispatch({
type: USER_UPDATE_PROFILE_REQUEST,
});
const {
userLogin: { userInfo },
} = getState();
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
};
console.log("UPDATE Action called");
const { data } = await axios.put(`/api/users/profile`, user, config);
dispatch({
type: USER_UPDATE_PROFILE_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: USER_UPDATE_PROFILE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
userReducer.js
import {
USER_UPDATE_PROFILE_FAIL,
USER_UPDATE_PROFILE_REQUEST,
USER_UPDATE_PROFILE_SUCCESS,
} from "../constants/userConstants";
export const userUpdateProfileReducer = (state = {}, action) => {
switch (action.type) {
case USER_UPDATE_PROFILE_REQUEST:
return { loading: true };
case USER_UPDATE_PROFILE_SUCCESS:
return { loading: false, success: true, userInfo: action.payload };
case USER_UPDATE_PROFILE_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
store.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
// reducers
import {
userLoginReducer,
userRegisterReducer,
userDetailsReducer,
userUpdateProfileReducer,
} from "./reducers/userReducers";
const reducer = combineReducers({
userLogin: userLoginReducer,
userRegister: userRegisterReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdateProfileReducer,
});
const userInfoFromStorage = localStorage.getItem("userInfo")
? JSON.parse(localStorage.getItem("userInfo"))
: null;
const initialState = {
userLogin: { userInfo: userInfoFromStorage },
};
const middleware = [thunk];
const store = createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
BACKEND
userRoutes.js
const express = require("express");
const {
authUser,
getUserProfile,
registerUser,
updateUserProfile,
} = require("../controllers/userController");
const protect = require("../middleware/authMiddleware");
const router = express.Router();
router.route("/").post(registerUser);
router.post("/login", authUser);
router
.route("/profile")
.get(protect, getUserProfile)
.put(protect, updateUserProfile);
module.exports = router;
userController.js
// #description: Update user profile
// #route: PUT /api/users/profile
// #access: Private
exports.updateUserProfile = async (req, res, next) => {
try {
const user = await User.findById(req.user._id);
if (user) {
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
_id: updatedUser._id,
name: updatedUser.name,
email: updatedUser.email,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser._id),
});
}
} catch (error) {
error = new Error("User not found");
error.status = 404;
next(error);
}
};
authMiddleware.js
const jwt = require("jsonwebtoken");
const User = require("../models/userModel");
const protect = async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
try {
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = await User.findById(decoded.id).select("-password");
next();
} catch (error) {
error = new Error("Not Authorized!!");
error.status = 401;
next(error);
}
}
if (!token) {
const error = new Error("Not Authorized!!, No Token!!");
error.status = 401;
next(error);
}
};
module.exports = protect;
The error is (most probably) in
const user = await User.findById(req.user._id);
check if you are getting the "req.user._id" correctly. And also check your authMiddlwere if there is any error in getting req.user._id .
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),
})
I want to do an edit on a form when clicking on a specific ID for a booking so that the values from my ID will be passed on to the edit form. But match.params returns empty even when URL contains params. How can I solve this?
This is my edit a booking file:
export default function AdminEdit(props) {
const [CategoryList, setCategory] = useState([]);
const [data, setData] = useState({
date: "",
firstName: "",
lastName: "",
phone: "",
email: "",
noPersons: "",
time: "",
});
useEffect(() => {
const _id = props.match.params.id;
console.log(props.match.params.id);
console.log(_id);
Axios.get(config.API_HOST_ORIGINAL + `/adminbookings/` + _id, {
headers: {
'Authorization': 'Bearer ' + config.getToken()
}
}).then(res => {
setData(res.data)
console.log("Logged res data:", res.data);
}).catch(err => console.error(err))
}, []);
function submit(e) {
e.preventDefault();
Axios.post(config.API_HOST_ORIGINAL + '/bookings/', {
headers: {
'Authorization': 'Bearer ' + config.getToken()
}
}).then(res => {
console.log(res.data);
const myData = [...CategoryList, res.data]
setCategory(myData)
}).catch(err => console.error(err))
}
function handle(e) {
const newData = { ...data }
newData[e.target._id] = e.target.value;
setData(newData);
}
**Another other file:
This is where I take in all the bookings**
const Bookings = (props) => {
Some hooks...
function Update(_id) {
console.log("Log this shiiiet to console", _id);
props.history.push("/adminedit/" + _id);
}
return {
<>
<td>
<button type="button"
onClick={() => Update(bookings._id)}
className="btn btn-primary btn-sm">Edit</button>
</td>
</>
}
export default Bookings;
Recives this warning!
You should use useParams hook from react-router-dom.
Also you should define id outside the useEffect
import {
useParams
} from "react-router-dom";
...
let { id } = useParams();
...
useEffect(() => {
}, [])
So basically, I am calling loadUser which gets the User from the backend, it consistently works fine but appearantly whenever i refresh Dashboard page it does not fire off any actions, even though i would try calling loadUser inside of the useEffect which is in Dashboard page, still it no actions gets fired and i do not have access to the user, this is something i need cause i have to have access to the users ID. Also I am using redux thunk, I heard there are side effects that do exists, but still i would truly love to get help :)
I will link the github repo down below and paste code that seem related to this issue. If you do need anymore code the repo is here too:
https://github.com/tigerabrodi/eBuy
Dashboard Component
import React, {useEffect, Fragment, useState} from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import Pagination from '../products/Pagination';
import ProductItem from '../products/ProductItem';
import { getUserProducts } from '../../redux/product/product.actions';
import {loadUser} from "../../redux/auth/auth.actions";
const Dashboard = ({product: {products, loading, totalProducts}, loadUser, getUserProducts, auth: {user}}) => {
const [currentPage, setCurrentPage] = useState(1);
const [productsPerPage] = useState(6);
const paginate = pageNumber => setCurrentPage(pageNumber);
useEffect(() => {
getUserProducts(user._id, currentPage);
}, [currentPage, getUserProducts, user._id]);
return (
<Fragment>
<div className="container">
<div className="row">
<div className="col text-center">
<h1 className="text-monospace text-info display-2">Dashboard</h1>
<Link to="/add-product" className="btn btn-block btn-warning">Add Product <i className="far fa-money-bill-alt" /> </Link>
</div>
</div>
</div>
<br />
<div className="container">
<div className="row">
{products.map(product => (
<div className="col-md-4 col-6">
<ProductItem key={product._id} product={product} />
</div>
))};
<div className="col-12">
{products && (
<Pagination productsPerPage={productsPerPage} totalProducts={totalProducts} paginate={paginate} />
)}
</div>
</div>
</div>
</Fragment>
);
}
const mapStateToProps = state => ({
product: state.product,
auth: state.auth
})
export default connect(mapStateToProps, {getUserProducts, loadUser})(Dashboard);
auth reducer
import {AuthActionTypes} from "./auth.types";
const initialState = {
token: localStorage.getItem("token"),
isAuthenticated: null,
loading: true,
user: null
}
const authReducer = (state = initialState, action) => {
const {type, payload} = action;
switch (type) {
case AuthActionTypes.USER_LOADED:
return {
...state,
isAuthenticated: true,
loading: false,
user: payload
};
case AuthActionTypes.REGISTER_SUCCESS:
case AuthActionTypes.LOGIN_SUCCESS:
localStorage.setItem('token', payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false
};
case AuthActionTypes.REGISTER_FAIL:
case AuthActionTypes.AUTH_ERROR:
case AuthActionTypes.LOGIN_FAIL:
case AuthActionTypes.LOGOUT:
case AuthActionTypes.ACCOUNT_DELETED:
case AuthActionTypes.USER_ERROR:
localStorage.removeItem('token');
return {
...state,
token: null,
isAuthenticated: false,
loading: false
};
default:
return state;
}
}
export default authReducer
auth actions
import axios from "axios";
import {setAlert} from "../alert/alert.actions"
import {AuthActionTypes} from "./auth.types"
import setAuthToken from "../../utils/setAuthToken"
// Load User
export const loadUser = () => async dispatch => {
if (localStorage.token) {
setAuthToken(localStorage.token);
}
try {
const res = await axios.get('/auth');
dispatch({
type: AuthActionTypes.USER_LOADED,
payload: res.data
});
} catch (err) {
dispatch({
type: AuthActionTypes.AUTH_ERROR
});
}
};
// Register User
export const register = ({ name, email, password }) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ name, email, password });
try {
const res = await axios.post('/auth/signup', body, config);
dispatch({
type: AuthActionTypes.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: AuthActionTypes.REGISTER_FAIL
});
}
};
// Login User
export const login = (email, password) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
const body = JSON.stringify({ email, password });
try {
const res = await axios.post('/auth/signin', body, config);
dispatch({
type: AuthActionTypes.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: AuthActionTypes.LOGIN_FAIL
});
}
};
// Logout / Clear Profile
export const logout = () => dispatch => {
dispatch({ type: AuthActionTypes.LOGOUT });
};