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')
Related
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 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}))
Hello I am making login app used firebase google login. I want to save the user in the user-reducer.
The console shows user info from the firebase! but, It keeps shows undefined. I don't know why it shows like these.
Please advised!
[user-action ]
import { UserType } from './user.types';
export const login = ({ currentUser }) => ({
type: UserType.USER_LOGIN,
payload: currentUser,
});
[user-types ]
export const UserType = {
USER_LOGIN: 'USER_LOGIN',
};
[user-reducer]
import { UserType } from './user.types';
const INITAIL_STATE = {
user: [],
};
const userReducer = (state = INITAIL_STATE, action) => {
switch (action.type) {
case UserType.USER_LOGIN:
return {
...state,
user: action.payload,
};
default:
return state;
}
};
export default userReducer;
[Login.js]
const Login = ({ googleLogin }) => {
const [currentUser, setCurrentUser] = useState(null);
const getUser = new Promise((resolve, reject) => {
console.log('doing...');
firebase.auth().onAuthStateChanged(setCurrentUser);
resolve(currentUser);
console.log(currentUser);
});
getUser
.then(user => {
googleLogin(user);
})
.catch(error => {
console.log(error);
});
....
const mapDispatchToProps = dispatch => ({
onModal: () => dispatch(modalHandler()),
googleLogin: currentUser => dispatch(login(currentUser)),
});
export default connect(null, mapDispatchToProps)(Login);
Your problem is on getUser, you are resolving promise passing a state variable (currentUser) that may have been not setted (because setCurrentUser is async). I would suggest to modify you code in this way:
const getUser = new Promise((resolve, reject) => {
console.log('doing...');
firebase.auth().onAuthStateChanged(firebaseUser => {
setCurrentUser(firebaseUser);
resolve(firebaseUser);
console.log(firebaseUser);
});
});
export const login = ({ currentUser }) => ({
type: UserType.USER_LOGIN,
payload: currentUser,
});
should be -
export const login = (currentUser) => ({
type: UserType.USER_LOGIN,
payload: currentUser,
});
i.e no destructuring of currentUser
OR
googleLogin: currentUser => dispatch(login(currentUser))
should be
googleLogin: currentUser => dispatch(login({currentUser}))
Being a newbie with RN and Redux, I'm confused as to why my props are undefined after reading from AsyncStorage.
I log in, save the state to the store and storage... I reload the app and read from the storage and update the state. The storage is retrieving my object but the props are undefined.
actions.js:
export const getSession = (data) => ({
type: 'GET_SESSION',
payload: {
user: data
}
});
export const getUserSession = () => dispatch => {
return AsyncStorage.getItem('userSession').then((data) => {
console.log('Props at asynsstorage: ', data);
// {"current_user":{"uid":"1","roles":["authenticated","administrator"], ...}
dispatch(loading(false));
dispatch(getSession(data));
})
.catch((err) => {
})
}
reducer.js
import { combineReducers } from 'redux';
const defaultState = {
xcsrf: '',
user: {},
loading: false,
error: '',
};
const authReducer = ( state = defaultState, action ) => {
switch(action.type) {
case 'GET_SESSION':
return {
...state,
user: action.payload.user,
loading: false,
}
case 'SAVE_SESSION':
return {
...state,
user: action.payload.user,
loading: false,
}
default:
return state;
}
}
export default combineReducers({
authReducer: authReducer
});
authLoading.js // screen
class AuthLoadingScreen extends React.Component {
constructor() {
super();
}
componentDidMount = () => {
this.props.getUserSession().then(() => {
console.log( 'Props at loading: ', this.props.user );
// undefined
})
.catch(error => {
})
};
// Render any loading content that you like here
render() {
return ();
}
}
const mapStateToProps = state => ({
user: state.user,
});
const mapDispatchToProps = dispatch => ({
getUserSession: () => dispatch(getUserSession()),
});
export default connect(mapStateToProps, mapDispatchToProps)(AuthLoadingScreen);
You cannot access directly user of reducer. So change
const mapStateToProps = state => ({
user: state.user,
});
To
const mapStateToProps = state => ({
user: state.authReducer.user,
});
And one more thing AsyncStorage's getItem() method return string of stored data. You have not converted to it json. So please also convert that as below :
export const getUserSession = () => dispatch => {
return AsyncStorage.getItem('userSession').then((data) => {
console.log('Props at asynsstorage: ', data);
// {"current_user":{"uid":"1","roles":["authenticated","administrator"], ...}
dispatch(loading(false));
dispatch(getSession(JSON.parse(data))); //convert to json here
})
.catch((err) => {
})
}
I have a working React app, including alert functionality, but when I try to compile for production it throws a type error.
I've included the code for my alert reducer, action, and component below:
Reducer
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';
const initialState = [{}];
export default function(state = initialState, action) {
const { type, payload } = action;
const merged = { ...initialState, ...state };
switch (type) {
case SET_ALERT:
return [...state, payload];
case REMOVE_ALERT:
return merged.filter(alert => alert.id !== payload);
default:
return state;
}
}
Action
import uuid from 'uuid';
import { SET_ALERT, REMOVE_ALERT } from './types';
export const setAlert = (msg, alertType, timeout = 5000) => dispatch => {
const id = uuid.v4();
dispatch({
type: SET_ALERT,
payload: { msg, alertType, id }
});
setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
};
Component
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
const Alert = ({ alerts }) =>
alerts !== null &&
alerts.length > 1 &&
alerts.slice(1).map(alert => (
<div key='alert.id' className={`alert alert-${alert.alertType}`}>
{alert.msg}
</div>
));
Alert.propTypes = {
alerts: PropTypes.array.isRequired
};
const mapStateToProps = state => ({
alerts: state.alert
});
export default connect(mapStateToProps)(Alert);
The error message is says:
./src/reducers/alert.js
TypeError: Cannot read property 'name' of null
at Array.filter (<anonymous>)
I had the exact same error as you , this is what i do :
import uuid from "uuid";
import { SET_ALERT, REMOVE_ALERT } from "../types";
export const setAlert = (msg, alertType, timeout = 5000) => dispatch => {
const id = uuid.v4();
dispatch({
type: SET_ALERT,
payload: [{ msg, alertType, id }]
});
setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
};
import { SET_ALERT, REMOVE_ALERT } from "../types";
const initialState = [];
export default function(state = initialState, { type, payload }) {
switch (type) {
case SET_ALERT:
return payload;
case REMOVE_ALERT:
return [];
default:
return state;
}
}
and do a clean git init and VoilĂ , this is my repo for the course https://github.com/syahmiyani/devlepak
Change your Reducer to below code. You need to give the function name.
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';
const initialState = [{}];
const alert = (state = initialState, action) => {
const { type, payload } = action;
const merged = { ...initialState, ...state };
switch (type) {
case SET_ALERT:
return [...state, payload];
case REMOVE_ALERT:
return merged.filter(alert => alert.id !== payload);
default:
return state;
}
};
export default alert;