Waiting for Context to update - reactjs

I am using React Context to store the logged-in users credentials and have created a reducer/action pattern to handle the logic. Right now, I'm able to successfully login with the proper credentials, but I also want to redirect the user to his profile with a dynamic param like so: profile/:id. My problem is, I'm not sure how to access the user's id in the same function as the initial call to Context where I populate it with the user's login credentials. So far, I have:
function Login(props) {
const userContext = useContext(UserContext);
const [credentials, setCredentials] = useState({
username: '',
password: ''
});
async function onSubmit (e) {
e.preventDefault();
const user = await userContext.loginUser(credentials); // Wait for payload...
props.push.history(`/profile${user.id}`); // ...Then use payload (user.id) to redirect to desired endpoint
}
For reference, here is my loginUser action:
const loginUser = async credentials => {
setLoading();
const response = await axios
.post('endpoint here', credentials)
dispatch({
type: LOGIN_USER,
payload: response.data
})
}
And here is my reducer with my initial state below it:
export default (state, action) => {
switch(action.type) {
case SET_LOADING:
return {
...state,
loading: true
}
case LOGIN_USER:
return {
...state,
user: action.payload,
loading: false
}
default:
return state;
}
}
const initialState = {
user: {},
issues: [],
loading: false
}
Hopefully, this is enough for someone to spot my shortcoming. Any help is appreciated.

Related

401 Unauthorized react axios

I am trying to make an ecommerce using react, redux toolkit and axios
the problem is that I want the user to log in and get his cart from the backend right after the login
it always fails the and says (unauthorized) when i first login because it can't find the token
then after refresh it says unauthorized one more time
after the third refresh it works
this is my get cart
export const getCart = createAsyncThunk("cart/getcart", async () => {
const response = await axios.get("http://127.0.0.1:8000/techcart/get_cart/", {
headers: {
Authorization: `Token ${token}`,
},
});
return response.data;
});
const cartSlice = createSlice({
name: "cart",
initialState: {
cart: [],
cartItemsIds :[],
},
builder.addCase(getCart.fulfilled, (state, action) => {
state.cart = action.payload;
and this is my login function
export const login = createAsyncThunk(
"auth/login",
async ({ email, password }, thunkAPI) => {
try {
const response = await axios.post(
"http://127.0.0.1:8000/techcart/login/",
{ username: email, password }
);
localStorage.setItem("user", JSON.stringify(response.data));
return response.data;
} catch (error) {}
}
);
const initialState = user
? { isLoggedIn: true, user }
: { isLoggedIn: false, user: null };
builder.addCase(login.fulfilled, (state, action) => {
state.isLoggedIn = true;
state.user = action.payload;
here is where i am doing the login
const HandleLogin = () => {
dispatch(login({ email, password }));
};
useEffect(()=> {
if(isLoggedIn){
navigate('/')
dispatch(getCart())
}
},[isLoggedIn])
Cart page
useEffect(() => {
dispatch(getCart());
}, []);
here is where im defining my token :
export let user = JSON.parse(localStorage.getItem("user")) ? JSON.parse(localStorage.getItem("user")) : null;
export let userId = user ? user.user_id : null;
export let token = user!=null ? user.token : null;
and here is where im importing it in my cart slice
import { user, token } from "../../constants";
im using redux persist to persist the state of my cart
if anyone can help me i'm so thankful
here is what happens
You're initializing your token directly when your js is executed. So when you retrieve it, it is undefined.
Ans when you do the login, you're indeed storing your token, but you're not updating it in your application.
I can see you're using redux, so store your token in your redux store, and before sending your api call to retrieve your cart, retrieve your token from redux, to always have the latest value of your token

How to add data to firestore document after creating user

I have a react application that should sign up a user and add the user's info to a collection using the uid. I am using redux and have broken my code into component, reducer and action. This is the add user component:
state = {
name : '',
email : '',
password : '',
position : '',
department : '',
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
this.props.signUp(this.state)
}
render() {
return(//I have my input fields and submit button here)
}
const mapStateToProps = (state) => {
return {
auth: state.firebase.auth
}
}
const mapDispatchToProps = (dispatch) => {
return {
signUp: (newUser) => dispatch(signUp(newUser))
}
}
export default connect (mapStateToProps, mapDispatchToProps)(AddUser);
I do my auth action in the authAction with:
export const signUp = (newUser) => {
return (dispatch, getState, {getFirebase, getFirestore}) => {
const firebase = getFirebase();
const firestore = getFirestore();
firebase.auth().createUserWithEmailAndPassword(
newUser.email,
newUser.password
).then((resp) =>{
return firestore.collection('users').doc(resp.user.uid).set({
name: newUser.name,
position: newUser.position,
department: newUser.department
})
}).then(() => {
dispatch({ type: 'SIGNUP_SUCCESS' })
}).catch(err => {
dispatch({ type: 'SIGNUP_ERROR', err})
})
}
}
my Sign Up reducer is:
case 'SIGNUP_SUCCESS':
console.log('Signup success');
return {
...state,
authError: null
}
case 'SIGNUP_ERROR':
console.log('signup error');
return {
...state,
authError: action.err.message
}
The issue is that this creates a user and logs the user in but doesn't create a document with the user data in the firestore collection. I do not get any errors on the console. it also doesn't log the success message. The user is created and can log in though.
What am I doing wrong? Please help.
Thanks!
Do I understand right that it is an async process? If yes, you should also use Redux-Saga library for this(I mean it would be better practice).
However, if you are not willing to implement that try adding debugger or console log data in sign up action before return to see if you passing the right data in the right format for firebase. Same goes to catch block, console log error, I may say useful information if the error is in request/response.

Getting error in Axois in catch and after dispatch value didn't getting in props

I'm new in Redux React and creating web app where app interact with Lumen API framework. When a request go to server and return with error code 400, 404, 500 any status code (error) except 200 it shows console error and processed after that in React.
I tried pass value when get error at axois.catch({dispatch}) but value update in state by viewing redux-dev-tool but didn't get value at props.
As from API I passed as like:
if ($validator->fails()) {
return response()->json(['type'=> 'error','message'=> $validator->errors()->all()],400);
}
And in my action file as like:
export const loginRequest = formValues => async dispatch => {
await user.post('/user/create', { ...formValues, device_id: 12345}).then(response => {
dispatch ({type: LOGIN, payload: response.data});
}).catch(error => {
if(error.response) {
dispatch ({ type: ERRORS, payload: error.response.data.message });
}
});
}
and in reducer:
const INTIAL_STATE = {
isSignedIn: null,
accessToken: null,
loginMessage: null,
errorMessage: null
};
export default (state = INTIAL_STATE, action) => {
switch (action.type){
case ERRORS:
return { ...state, isSignedIn: false, accessToken:null , errorMessage: action.payload };
default:
return state;
}
};
as last in my component file:
const mapStateToProps = state => {
return {error: state.auth.errorMessage};
};
and console log in render method:
console.log(this.props);
I'm getting POST http://localhost:8000/user/create 500 (Internal Server Error) in my console but the errorMessage value updated in state as looked in redux-dev-tool and after error no code run.
Use the redux-saga-routines for dispatching actions, make your work easy with this module.
Here its documentation link https://github.com/afitiskin/redux-saga-routines

Redux Action not returning JSON?

I'm struggling to figure out why my redux action is not returning the JSON from the GET request, even though when I submit the GET request in Postman, I can access the information?
The error I have returning is: Profile Not Found. Yet, like I said when I do the Postman request, it's working fine.
This Redux Action doesn't work:
// Get profile by id for admins
export const getUserProfile = (id) => dispatch => {
dispatch(setProfileLoading());
axios.get(`/admin/profile/${id}`)
.then(res =>
dispatch({
type: GET_PROFILE,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
}
Here is the Admin route which works in Postman and is returning the JSON?
router.get('/admin/profile/:id', passport.authenticate('jwt', {
session: false
}), (req, res) => {
const errors = {};
User.findOne({
user: req.user.id
})
.then(user => {
if (req.user.role === 'admin') {
Profile.findById(req.params.id)
.then(profile => {
res.json(profile);
})
.catch(err => res.status(404).json({
profilenotfound: 'Profile not found'
}));
} else {
res.json({unauthorized: 'User is unauthorized to view this area'})
}
})
.catch(err => res.status(404).json(err));
});
Here is the reducer:
import { GET_PROFILE, PROFILE_LOADING, CLEAR_CURRENT_PROFILE, GET_PROFILES }
from '../actions/types';
const initialState = {
profile: null,
profiles: null,
loading: false
}
export default function(state = initialState, action) {
switch(action.type) {
case PROFILE_LOADING:
return {
...state,
loading: true
}
case GET_PROFILE:
return {
...state,
profile: action.payload,
loading: false
}
case GET_PROFILES:
return {
...state,
profiles: action.payload,
loading: false
}
case CLEAR_CURRENT_PROFILE:
return {
...state,
profile: null
}
default:
return state;
}
}
As mentionned in the comments the problem is that you are not passing the id, you need to pass the id when you call your Redux action in your component for example if you call your getUserProfile method it should be something like that:
componentDidMount() {
const {getUserProfile} = this.props; // This is destructuring for better readability
// Here you need to pass your id for example 1234 or get it from params or from wherever you want...
getUserProfile(1234);
}

Dispatch multiple actions using Redux Thunk and the await/async syntax to track loading

I am currently editing some reducers to be able to track the loading state of axios operations. Most of my async syntax is written in async/await fashion and would like to keep it that way to keep my code organized.
I am not sure how to dispatch two action creators one after the other: the first one to fire off the FETCHING_USER action type and keep track of the reduced isFetching state, while the other one to fire off the actual axios GET request. The code currently looks like this to get the API request:
export const fetchUser = () => async dispatch => {
const res = await axios.get(`${API_URL}/api/current_user`, {
headers: { authorization: localStorage.getItem("token") }
});
dispatch({ type: FETCH_USER, payload: res.data });
};
I am not sure how to dispatch the FETCHING_USER and then fire off the FETCH_USER action.
First you need to modify your reducer to have isFetching statement and requesting and receiving data cases:
const INITIAL_STATE = { isFetching: false, data: [] };
export default(state = INITIAL_STATE, action) => {
switch(action.type) {
case REQUEST_USER: {
return {...state, isFetching: true};
}
case RECEIVE_USER: {
return {...state, isFetching: false, data: action.payload};
}
default: return state;
}
}
Then modify your action to use try/catch statements:
export const fetchUser = () => async dispatch => {
dispatch({ type: REQUEST_USER });
try {
const res = await axios.get(`${API_URL}/api/current_user`, {
headers: { authorization: localStorage.getItem("token") }
});
dispatch({ type: RECEIVE_USER, payload: res.data });
}
catch(e){
//dispatch your error actions types, (notifications, etc...)
}
};
Then in component you can use condition like: isFetching ? //show loader : //show content (data[])

Resources