redux state gets erased after page refresh(redux-toolkit) - javascript

userSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import { API } from "../axios/index";
export const signUp = createAsyncThunk("users/signup", async (params) => {
try {
const { formData, dispatch, history } = params;
const { data } = await API.post("/users/signup", formData);
history.push("/");
dispatch(handleExistEmail(false));
return data;
} catch (error) {
console.log("aha hata var");
const { dispatch } = params;
const { status } = error.response;
if (status) {
dispatch(handleExistEmail(true));
}
}
});
export const logOut = createAsyncThunk("users/logout", async (params) => {
try {
const { id } = params;
const { data } = await API.put(`users/logout/${id}`);
localStorage.removeItem('user')
return data;
} catch (error) {
console.log(error);
}
});
const initialState = {
usersInfo: {},
status: "idle",
error: null,
existEmail: false,
};
const usersSlice = createSlice({
name: "users",
initialState,
reducers: {
handleExistEmail: (state, action) => {
state.existEmail = action.payload;
},
},
extraReducers: {
[signUp.pending]: (state, action) => {
state.status = "loading";
},
[signUp.fulfilled]: (state, action) => {
state.status = "succeeded";
state.usersInfo = action.payload;
localStorage.setItem("user", JSON.stringify(action.payload));
},
[signUp.error]: (state, action) => {
state.status = "failed";
state.error = "error";
},
},
});
export default usersSlice.reducer;
export const { handleExistEmail } = usersSlice.actions;
Auth.jsx
import React, { useState } from "react";
import { Container, Row, Col, Form, Button } from "react-bootstrap";
import { signUp } from "../features/usersSlice";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
const AuthScreen = ({ history }) => {
const existEmail = useSelector((state) => state.users.existEmail);
const dispatch = useDispatch();
const [login, setLogin] = useState(true);
const [formData, setFormData] = useState({
email: "",
password: "",
confirmPassword: "",
firstName: "",
lastName: "",
});
const handleSignUp = (e) => {
console.log("kayıt olma işlemi çalıştı")
if (formData.password === formData.confirmPassword) {
e.preventDefault();
dispatch(signUp({ formData, dispatch, history }));
} else {
e.preventDefault();
}
};
return (
<>
<Container>...
</>
);
};
export default AuthScreen;
userRouter.js
import express from "express";
import User from "../models/userModel.js";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import tokenModel from "../models/tokenModel.js";
const router = express.Router();
router.post("/signup", async (req, res) => {
try {
const { email, password, confirmPassword, firstName, lastName } = req.body;
const userExists = await User.findOne({ email });
if (userExists) {
console.log("A user with this email already exists");
return res
.status(400)
.json({ message: "A user with this email already exists" });
}
if (password !== confirmPassword) {
return res.status(400).json({ message: "Passwords don't match" });
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = await User.create({
email,
name: `${firstName} ${lastName}`,
password: hashedPassword,
});
const accessToken = jwt.sign(
{ email: user.email, id: user._id },
process.env.ACCESS_TOKEN_SECRET,
{
expiresIn: "3m",
}
);
const refreshToken = jwt.sign(
{ email: user.email, id: user._id },
process.env.REFRESH_TOKEN
);
await tokenModel.create({
userId: user._id,
refreshToken: refreshToken,
});
res.status(200).json({ user, accessToken });
} catch (error) {
console.log(error);
}
});
Hi all.I try to fill my redux state when i sign up with users infos and it works well when i sign up. But when i refresh page my redux state gets erased. I used redux-persist but it didn't work either. I have searched many websites for solution to fix it but everyone suggest redux-persist but do i have to use a ready-made package ? what is the source of this issue ?

Related

How to get blob image from MySQL to react component

I need assistance on fetching my blob image from MySQL to browser.
I am using redux toolkit. So far, these are what I have. When I dispatch the imageId, I get isSuccess to be true on my Redux Dev Tool but I don't seem to know how to get the image to my browser.
Please, kindly help me solve this. I am stuck.
Thanks
CONTROLLER FILE on profileController.js
const getProfilePicture = async (req, res) => {
try {
const base64Data = await ProfilePic.findByPk(req.params.imageId);
res.send(base64Data);
} catch (error) {
res.status(500).send("error.message");
}
};
What I get on Postman when I hit the controller file route - the Database contains::: id(imageId), avatar(Blob Image), Description, createdAt, UpdatedAt
{
"id": "6dc38579-4e0f-4d4b-8777-7b6527408e72",
"avatar": {
"type": "Buffer",
"data": [
91,
111,
98,
106,
101,
99,
116,
32,
79,
98,
106,
101,
99,
116,
93
]
},
"description": null,
"createdAt": "2023-02-17T09:22:28.000Z",
"updatedAt": "2023-02-17T09:28:00.000Z"
}
SERVICE FILE on getProfilePictureService.js
import axios from "axios";
const API_URL = "http://localhost:4000/api/profile/";
const getProfilePicture = async (imageId, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
responseType: "arraybuffer",
};
try {
const response = await axios.get(
API_URL + "getProfilePicture/" + imageId,
config
);
const imageBuffer = Buffer.from(response.data, "binary");
const imageBlob = new Blob([imageBuffer], { type: "image/jpg" });
return URL.createObjectURL(imageBlob);
} catch (error) {
console.log(error);
}
};
const getProfilePictureService = {
getProfilePicture,
};
export default getProfilePictureService;
SLICE FILE on getProfilePictureSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import getProfilePictureService from "./getProfilePictureService";
const initialState = {
getProPics: [],
isLoading: false,
isSuccess: false,
isError: false,
message: "",
};
//get profile picture
export const getProfilePicture = createAsyncThunk(
"profile/picture",
async (imageId, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await getProfilePictureService.getProfilePicture(imageId, token);
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const getProfilePictureSlice = createSlice({
name: "getProPic",
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(getProfilePicture.pending, (state) => {
state.isLoading = true;
})
.addCase(getProfilePicture.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.getProPics = action.payload;
})
.addCase(getProfilePicture.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
});
},
});
export const { reset } = getProfilePictureSlice.actions;
export default getProfilePictureSlice.reducer;
THE PROFILE BAR COMPONENT where I intend using the image ProfileBar.js
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { getProfilePicture, reset } from "./features/getProfilePicture/getProfilePictureSlice";
const ProfileBar = ({ profile }) => {
const dispatch = useDispatch();
const [imageUrl, setImageUrl] = useState(null);
const imageId = profile.id; //I placed the profile id (which is the same as the image primary key in the database) in a constant
useEffect(() => {
dispatch(getProfilePicture(imageId)); //I dispatch that id here. I get isSuccess to be true after dispatching
return () => {
dispatch(reset());
};
}, [dispatch]);
useEffect(() => {
async function fetchImage() {
const url = await getProfilePicture(imageId);
setImageUrl(url); //I intended to use this to set the state after dispatching and isSuccess is true
}
fetchImage();
}, [imageId]);
return (
<div>
<img src={imageUrl} alt="Image" /> //this is supposed to get the image on browser
</div>
);
};
export default ProfileBar;

Code doesnt run after having 2 hooks with null properties

This part of the code is not working after I updated react scripts from 2.0 to 5.0.
const { user, dispatch } = useContext(AuthContext);
const { data } = useFetch(`/contracts/${user.contractType}`);
if (!user) {
return <Navigate to="/" />;
}
What I want to happen is if a user that isn't logged in tries to access this page via URL, they get redirected to the main website page.
The error that I get from the console:
TypeError: Cannot read properties of null (reading 'contractType')
at Userinfo (userinfo.js:25:1)
Here is the full code of the page
import React, { useContext } from "react";
import { useState } from "react";
import useFetch from "../../hooks/useFetch";
import Footer from "../../components/OutFooter";
import Navbar from "../../components/OutNavbar";
import Sidebar from "../../components/OutSidebar";
import {
ContractContainer,
HeadingContainer,
TypeH1,
ActiveUntil,
MonthlyWrapper,
MonthlyContainer,
MonthNumber,
Price,
Navbarback,
} from "./userinfoElements";
import { AuthContext } from "../../context/AuthContext";
import { Navigate } from "react-router-dom";
import moment from "moment";
import axios from "axios";
const Userinfo = () => {
const { user, dispatch } = useContext(AuthContext);
const { data } = useFetch(`/contracts/${user.contractType}`);
// for nav bars
const [isOpen, setIsOpen] = useState(false);
// set state to true if false
const toggle = () => {
setIsOpen(!isOpen);
};
if (!user) {
return <Navigate to="/" />;
}
let dateFormat = moment(user.activeUntil).format("DD/MMMM/yyyy");
const update1Month = async () => {
try {
let newDate = moment(user.activeUntil).add(30, "days");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
};
const update3Month = async () => {
try {
let newDate = moment(user.activeUntil).add(90, "days");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
};
const update6Month = async () => {
try {
let newDate = moment(user.activeUntil).add(180, "days");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
};
const update12Month = async () => {
try {
let newDate = moment(user.activeUntil).add(365, "days");
dateFormat = newDate.format("DD/MMMM/yyyy");
await axios.put(`/activedate/${user.namekey}`, {
activeUntil: newDate,
});
dispatch({ type: "UPDATE_USER_DATE", payload: newDate });
} catch (err) {
console.log(err);
}
};
return (
<>
<Sidebar isOpen={isOpen} toggle={toggle} />
{/* navbar for smaller screens*/}
<Navbar toggle={toggle} />
<Navbarback /> {/* filling for transparent bacground navbar*/}
<>
<ContractContainer>
<TypeH1>
Hello {user.fName} {user.lName}!
</TypeH1>
<HeadingContainer>
<TypeH1>{data.contractType}</TypeH1>
<ActiveUntil>Subscription active until {dateFormat}</ActiveUntil>
</HeadingContainer>
<MonthlyWrapper>
<MonthlyContainer>
<MonthNumber>1 Month</MonthNumber>
<Price onClick={update1Month}>{data.month1Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>3 Month</MonthNumber>
<Price onClick={update3Month}>{data.month3Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>6Month</MonthNumber>
<Price onClick={update6Month}>{data.month6Price}$</Price>
</MonthlyContainer>
<MonthlyContainer>
<MonthNumber>12Month</MonthNumber>
<Price onClick={update12Month}>{data.month12Price}$</Price>
</MonthlyContainer>
</MonthlyWrapper>
</ContractContainer>
</>
<Footer />
</>
);
};
export default Userinfo;
Addition:
The code works as I want it to when I add the if statement between the 2 hooks like this
const { user, dispatch } = useContext(AuthContext);
if (!user) {
return <Navigate to="/" />;
}
const { data } = useFetch(`/contracts/${user.contractType}`);
But then I get an error that react hook is being used conditionally. In the case before this where the if statement is used after the hooks, the code after const { data } = useFetch(/contracts/${user.contractType}); doesn't run (tried with console.log), is there a way I can make the statement at least get recognized after the useFetch hook is used? Or if not possible, is there a way to make it so that when a console error happens I can redirect to the main page or ignore the console error?
EDIT:
Adding files relevant to this.
AuthContext
import React from "react";
import { createContext, useEffect, useReducer } from "react";
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
loading: false,
error: null,
};
export const AuthContext = createContext(INITIAL_STATE);
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null,
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null,
loading: false,
error: action.payload,
};
case "LOGOUT":
return {
user: null,
loading: false,
error: null,
};
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser,
};
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.user));
}, [state.user]);
return (
<AuthContext.Provider
value={{
user: state.user,
loading: state.loading,
error: state.error,
dispatch,
}}
>
{children}
</AuthContext.Provider>
);
};
useFetch
import { useEffect, useState } from "react";
import axios from "axios";
const useFetch = (url) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
fetchData();
}, [url]);
const reFetch = async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
};
return { data, loading, error, reFetch };
};
export default useFetch;
API users mongoose schema
import mongoose from "mongoose";
const UserSchema = new mongoose.Schema({
fName: { type: String },
lName: { type: String },
namekey: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
contractType: {
type: String,
},
activeUntil: {
type: Date,
},
});
export default mongoose.model("User", UserSchema);
API contract mongoose schema
import mongoose from "mongoose";
const ContractsSchema = new mongoose.Schema({
contractType: {
type: String,
},
speed: {
type: Number,
},
month1Price: {
type: Number,
},
month3Price: {
type: Number,
},
month6Price: {
type: Number,
},
month12Price: {
type: Number,
},
promote: {
type: Boolean,
},
});
export default mongoose.model("Contracts", ContractsSchema);
API authentication of users
import User from "../models/User.js";
import bcrypt from "bcryptjs";
import { createError } from "../utils/error.js";
import jwt from "jsonwebtoken";
// export const register = async (req, res, next) => {
// try {
// const salt = bcrypt.genSaltSync(13);
// const hash = bcrypt.hashSync(req.body.password, salt);
// const newUser = new User({
// ...req.body,
// password: hash,
// });
// await newUser.save();
// res.status(200).send("User has been created.");
// } catch (err) {
// next(err);
// }
// };
export const login = async (req, res, next) => {
try {
const user = await User.findOne({ namekey: req.body.namekey });
if (!user) return next(createError(404, "User not found!"));
if (req.body.password === undefined) {
return next(createError(500, "Wrong password or namekey!"));
}
const isPasswordCorrect = await bcrypt.compare(
req.body.password,
user.password
);
if (!isPasswordCorrect)
return next(createError(400, "Wrong password or namekey!"));
const token = jwt.sign({ id: user._id }, process.env.JWT);
const { password, ...otherDetails } = user._doc;
res
.cookie("access_token", token, {
httpOnly: true,
})
.status(200)
.json({ details: { ...otherDetails } });
} catch (err) {
next(err);
}
};
App.js routes
import React from "react";
import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/index";
import Signin from "./pages/signin/Signin";
import Userinfo from "./pages/userInfo/userinfo";
import PageNotFound from "./pages/PageNotFound";
function App() {
return (
<BrowserRouter>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/signin" element={<Signin />} />
<Route exact path="/myinfo" element={<Userinfo />} />
<Route path="*" element={<PageNotFound />} />
</Routes>
</BrowserRouter>
);
}
export default App;
src index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { AuthContextProvider } from "./context/AuthContext";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<AuthContextProvider>
<App />
</AuthContextProvider>
</React.StrictMode>
);
The initial user state is potentially null:
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
...
};
The AuthReducer functional also sets the user state to null in several cases:
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null, // <-- here
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null, // <-- here
loading: false,
error: action.payload,
};
case "LOGOUT":
return {
user: null, // <-- here
loading: false,
error: null,
};
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser,
};
default:
return state;
}
};
With the existing code you should account for the potentially null user state object by using a null-check/guard-clause when passing the value to the useFetch hook.
Example:
const { user, dispatch } = useContext(AuthContext);
const { data, reFetch } = useFetch(`/contracts/${user?.contractType}`);
...
const useFetch = (url) => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(false);
const const fetchData = React.useCallback(async () => {
setLoading(true);
try {
const res = await axios.get(url);
setData(res.data);
} catch (err) {
setError(err);
}
setLoading(false);
}, [url]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, loading, error, reFetch: fetchData };
};

I got a rejection error from the redux toolkit while trying to update the item

I am working on a MERN app and I have a problem when updating items. I am getting rejections when sending a patch request and there is not much info for debugging to solve the problem. I will appreciate it if someone can point out some logic that is not correct in my code. Thank you in advance.
Here below is the logic I have implemented.
postService.js:
import axios from 'axios';
const API_URL = '/api/posts/';
const updatePost = async (postId, postData, token) => {
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
};
const response = await axios.patch(`${API_URL}/${postId}/`, postData, config);
if (response.data) {
return {
...response.data,
id: postId,
};
}
};
postSlice.js:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import postService from './postService';
const initialState = {
posts: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
};
export const updatePost = createAsyncThunk(
'posts/updatePost',
async (id, postData, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await postService.updatePost(id, postData, token);
} catch (error) {
const message =
(error.response.data.message) ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const postSlice = createSlice({
name: 'post',
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(updatePost.pending, (state) => {
state.isLoading = true;
})
.addCase(updatePost.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
state.posts = state.posts.map((post) =>
post.id === action.payload.id ? action.payload : post
);
})
.addCase(updatePost.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
});
},
});
export const selectAllPosts = (state) => state.posts.posts;
export const { reset } = postSlice.actions;
export default postSlice.reducer;
Form.js:
const Form = ({ postId, setPostId }) => {
const [formData, setFormData] = useState({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
const dispatch = useDispatch();
const user = JSON.parse(localStorage.getItem('user'));
const post = useSelector((state) =>
postId ? state.posts.posts.find((post) => post._id === postId) : null
);
useEffect(() => {
if (post) setFormData(post);
}, [post]);
const clearPost = () => {
setPostId(0);
setFormData({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
};
const handleSubmit = async (e) => {
e.preventDefault();
if (
!formData.postCreator &&
!formData.title &&
!formData.body &&
!formData.imageFile
) {
toast.warning(
'Please fill out all fields, and make sure you are also logged in'
);
} else if (postId) {
dispatch(updatePost(postId, formData));
console.log(postId);
} else {
dispatch(createPost(formData));
clearPost();
setPostId(null);
}
clearPost();
};
The second param of createAsyncThunk is the payloadCreator.
The first param of the payloadCreator is the arguments. The second param of payloadCreator is thunkAPI.
So you should combine id and postData into a single object to represent the arguments.
Update postSlice.js:
export const updatePost = createAsyncThunk(
'posts/updatePost',
async ({id, postData}, thunkAPI) => {
try {
const token = thunkAPI.getState().auth.user.token;
return await postService.updatePost(id, postData, token);
} catch (error) {
const message =
(error.response.data.message) ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
Update where you dispatch the updatePost thunk:
updatePost({
id: 123,
postData: {
foo: 'bar'
}
})

I am getting undefined while sending patch request

I am working on a MERN app with redux toolkit. Currently, I am facing a problem with my update functionality, when I click on the update button I can see in redux dev tools the request is rejected and in the console, the id is showing undefined while I am passing it. I am probably missing something in my code, if someone can point it out and explain that would be great. Thanks in advance. Here below are my code:
postService.js:
import axios from 'axios';
const API_URL = '/api/posts/';
const updatePost = async (_id, postData) => {
const response = await axios.patch(API_URL + _id, postData);
return response.data;
};
const postService = {
updatePost,
};
export default postService;
postSlice.js:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import postService from './postService';
const initialState = {
posts: [],
isError: false,
isSuccess: false,
isLoading: false,
message: '',
};
export const updatePost = createAsyncThunk(
'posts/updatePost',
async ({ id, postData }, thunkAPI) => {
const { postCreator, title, body, imageFile } = postData;
try {
return await postService.updatePost(id, {
postCreator,
title,
body,
imageFile,
});
} catch (error) {
console.log(error.message);
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
return thunkAPI.rejectWithValue(message);
}
}
);
export const postSlice = createSlice({
name: 'post',
initialState,
reducers: {
reset: (state) => initialState,
},
extraReducers: (builder) => {
builder
.addCase(updatePost.pending, (state) => {
state.isLoading = true;
})
.addCase(updatePost.fulfilled, (state, action) => {
state.isLoading = false;
state.isSuccess = true;
const { id, postCreator, title, body, imageFile } = action.payload;
const existingPost = state.find((post) => post.id === id);
if (existingPost) {
existingPost.postCreator = postCreator;
existingPost.title = title;
existingPost.body = body;
existingPost.imageFile = imageFile;
}
})
.addCase(updatePost.rejected, (state, action) => {
state.isLoading = false;
state.isError = true;
state.message = action.payload;
})
export default postSlice.reducer;
Form.js:
const Form = ({ activeId, setActiveId }) => {
const [postData, setPostData] = useState({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
const post = useSelector((state) =>
activeId ? state.posts.posts.find((post) => post._id === activeId) : null
);
const user = JSON.parse(localStorage.getItem('user'));
const dispatch = useDispatch();
useEffect(() => {
if (post) setPostData(post);
}, [post]);
const clearInputField = () => {
setActiveId(0);
setPostData({
postCreator: '',
title: '',
body: '',
imageFile: '',
});
};
const handleSubmit = async (e) => {
e.preventDefault();
if (activeId) {
dispatch(updatePost({ activeId, postData }));
clearInputField();
} else {
dispatch(createPost(postData));
clearInputField();
}
};
In the updatePost thunk in postSlice.js, you are attempting to destructure the variables { id, postData } from the payload creator args.
But in Form.js, you are sending an object { activeId, postData } when you dispatch updatePost.
So both id and postData will be undefined because neither exist on the object.
You could change it to:
dispatch(updatePost({id: activeId, postData: formData}))

how to store the currentUser using redux and firebase

My problem is I cannot store the currentUser when I login with google , previously I have used only react to store the currentUser when he login and set null when he signout . So what i want is to store the currentUser
this is my code :
class HomeHeaderW extends React.Component {
authListener = null;
componentDidMount() {
const { setCurrentUser, currentUser } = this.props
this.authListener = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
console.log(currentUser)
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot(snapshot => {
setCurrentUser({
id: snapshot.id,
...snapshot.data()
});
})
}
setCurrentUser(userAuth)
})
}
render() {
const { currentUser } = this.props
return (...
}
HomeHeaderW.defaultProps = {
currentUser: null
};
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser
})
export default connect(mapStateToProps, null)(HomeHeaderW)
user.types.js
const userTypes = {
SET_CURRENT_USER: 'SET_CURRENT_USER'
};
export default userTypes
user.reducer.js
import userTypes from "./user.types";
const INITIAL_STATE = {
currentUser: null
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case userTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload
}
default:
return state;
}
};
export default userReducer
user.actions.js
import userTypes from "./user.types";
export const setCurrentUser = user =>
({
type: userTypes.SET_CURRENT_USER,
payload: user
})
utils.js
export const handleUserProfile = async({ userAuth, additionalData }) => {
if (!userAuth) return;
const { uid } = userAuth;
const userRef = firestore.doc(`users/${uid}`);
const snapshot = await userRef.get();
if (!snapshot.exists) {
const { displayName, email } = userAuth;
const timestamp = new Date();
const userRoles = ['user'];
try {
await userRef.set({
displayName,
email,
createdDate: timestamp,
userRoles,
...additionalData
});
} catch (err) {
console.log(err);
}
}
return userRef;
};
So when I check the currentUser in the console i see null
Also I get another problem in the console :
HomeHeaderW.js:28 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onSnapshot')

Categories

Resources