I am working on an assignment involving simple auth app in MERN stack. Everything is set just one problem is occurring when I am calling the UpdateUser function from another file it is not getting read/recognized by React. Also, when I import another function from the same file i.e logoutUser, it is working perfectly fine.
Dashboard.js-File where function is imported
import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { logoutUser } from "../../actions/authActions";
import { UpdateUser } from "../../actions/authActions";
import classnames from "classnames";
import M from "materialize-css";
import "react-phone-number-input/style.css";
class Dashboard extends Component {
constructor() {
super();
this.state = {
age: "",
gender: "",
dob: "",
mobile: "",
errors: {},
};
this.onValueChange = this.onValueChange.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({
errors: nextProps.errors,
});
}
}
onChange = (e) => {
this.setState({ [e.target.id]: e.target.value });
};
onValueChange(event) {
this.setState({
selectedOption: event.target.value,
gender: event.target.value,
});
console.log(this.state.selectedOption);
}
onDateChange = (val) => {
val = val.toString();
this.setState({ dob: val });
console.log(val, typeof val);
};
onMobileChange = (value) => {
this.setState({ mobile: value });
console.log(this.state.mobile);
};
onUpdateClick = (e) => {
e.preventDefault();
const UpdatedUser = {
id: this.props.auth.user.id,
age: this.state.age,
gender: this.state.gender,
dob: this.state.dob,
mobile: this.state.mobile,
};
console.log(UpdatedUser);
this.props.UpdateUser(UpdatedUser, this.props.history);
};
onLogoutClick = (e) => {
e.preventDefault();
this.props.logoutUser();
};
componentDidMount() {
var context = this;
var options = {
defaultDate: new Date(),
setDefaultDate: true,
onSelect: function(date) {
context.onDateChange(date);
// Selected date is logged
},
};
var elems = document.querySelector(".datepicker");
var instance = M.Datepicker.init(elems, options);
// instance.open();
instance.setDate(new Date());
}
render(){
return(JSX)
}
authActions.js- File from where the function is imported
import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";
import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from "./types";
// Register User
export const registerUser = (userData, history) => (dispatch) => {
axios
.post("/api/users/register", userData)
.then((res) => history.push("/login"))
.catch((err) =>
dispatch({
type: GET_ERRORS,
payload: err.response.data,
})
);
};
//Update User
export const UpdateUser = (userData, history) => (dispatch) => {
axios
.post("/api/users/update", userData)
.then((res) => history.push("/login"))
.catch((err) =>
dispatch({
type: GET_ERRORS,
payload: err.response.data,
})
);
};
// Login - get user token
export const loginUser = (userData) => (dispatch) => {
axios
.post("/api/users/login", userData)
.then((res) => {
// Save to localStorage
// Set token to localStorage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
// Set token to Auth header
setAuthToken(token);
// Decode token to get user data
const decoded = jwt_decode(token);
// Set current user
dispatch(setCurrentUser(decoded));
})
.catch((err) =>
dispatch({
type: GET_ERRORS,
payload: err.response.data,
})
);
};
// Set logged in user
export const setCurrentUser = (decoded) => {
return {
type: SET_CURRENT_USER,
payload: decoded,
};
};
// User loading
export const setUserLoading = () => {
return {
type: USER_LOADING,
};
};
// Log user out
export const logoutUser = () => (dispatch) => {
// Remove token from local storage
localStorage.removeItem("jwtToken");
// Remove auth header for future requests
setAuthToken(false);
// Set current user to empty object {} which will set isAuthenticated to false
dispatch(setCurrentUser({}));
};
One more thing to add, when I call the function directly instead of using this.props.UpdateUser, it is getting recognized by React and the error is gone too but the content of the function is not executing. PLEASE HELP I DONT HAVE MUCH TIME FOR THIS PROJECT SUBMISSION.
Neither of your imports do anything in this file. They are never called. What gets called are similar functions that are passed down to this component as props from some parent component. In your parent component you are passing logoutUser but forgetting to pass updateUser. Find the root file where logoutUser is imported and add updateUser to it.
Related
While I am trying to develop mern ec site.
This error has occurred on the sign in page.
what I have tried
1 Cookies->Cookie, set->Set
2 res.refresh_token->'res.refresh_token'
3 downgrade js-cookies version from 3 to 2.2.1
Cookies.set('refreshtoken', res.refresh_token, {
path: 'api/auth/accessToken',
expires: 7
})
all code(except html)
import Head from "next/head"
import Link from "next/link"
import { useState, useContext } from "react"
import { DataContext } from "../store/GlobalState"
import { postData } from "../utils/fetchData"
import { Cookies } from 'js-cookie'
const Signin = () => {
const initialState = { email: '', password: '' }
const [userData, setUserData] = useState(initialState)
const { email, password } = userData
const { state, dispatch } = useContext(DataContext)
const handleChangeInput = e => {
const { name, value } = e.target
setUserData({ ...userData, [name]: value })
dispatch({ type: 'NOTIFY', payload: {} })
}
const handleSubmit = async e => {
e.preventDefault()
dispatch({ type: 'NOTIFY', payload: { loading: true } })
const res = await postData('auth/login', userData)
if (res.err) return dispatch({ type: 'NOTIFY', payload: { error: res.err } })
dispatch({ type: 'NOTIFY', payload: { success: res.msg } })
dispatch({
type: 'AUTH', payload: {
token: res.access_token,
user: res.user
}
})
Cookies.set('refreshtoken', res.refresh_token, {
path: 'api/auth/accessToken',
expires: 7
})
localStorage.setItem('firstLogin', true)
}
}
export default Signin
As per the docs for js-cookie, your import statement is a little off. Try this:
import Cookies from 'js-cookie'
The way you are importing it, it is return undefined as the Cookies object is the default import, and not nested inside the module exports.
I'm working on a React project and I reuse a fetchAPIcall action since I make 3 different initial API calls, and further, I plan on using more to add and edit my Items.
So to have control over the correct order of the API call I tried using a flag at the end, being a state of the component.
And since I am using many API calls, to add some Items to favorites and be removed quickly by a like button, I'd like to know what is the best practice when using many async functions or API calls?
I can think of only 1) using flags, and 2) having the API call-actions separate for each, but in my case that would be a lot of code (get user)(get, add, delete FavList)(get Items)(add, edit remove Item).
By the way, the API is mine, made it in rails.
Here are the main code&files for my issue:
This is from my GetItemsNFavlist Component, this is where I load all the info of items and favList items. I made it into a component that I call because I thought it was a good idea so when I add an Item to the Favorites List I can just call this component to update my FavoritesList (but that 'updating' part isn't working great just yet, I'm having to go back to the User and again to the Fav List to see the update or even logout and in again to see the change).
Here I call the action "fetchAPIcall" and I check the status and response data with the "fetchCall" store object. Also here I do 2 API calls, 1) to get all the Items and 2) to get the FavoritesList for the User:
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import * as MyActions from '../actions';
const GetItemsNFavlist = props => {
const {
actions, items, fetchCall, favList, user,
} = props;
const [apiFlag, setApiFlag] = useState({ itm: false, fvl: false });
const itemsUrl = 'https://findmyitem-api.herokuapp.com/items';
const favListUrl = `https://findmyitem-api.herokuapp.com/users/${user.id}/favorites_lists`;
useEffect(() => { // #1
if (!apiFlag.itm && !apiFlag.fvl) actions.fetchAPIcall(itemsUrl, 'get', {});
}, []);
useEffect(() => {
if (!fetchCall.apiData && items[0]) {
actions.fetchAPIcall(favListUrl, 'get', {});
setApiFlag({ itm: true, fvl: false });
}
}, [items]);
useEffect(() => {
if (fetchCall.apiData && !items[0] && !favList[0]) {
actions.setItems(fetchCall.apiData);
actions.fetchAPIreset();
}
if (apiFlag.itm && fetchCall.apiData && !favList[0]) actions.setFavList(fetchCall.apiData);
});
useEffect(() => {
if (favList[0]) {
actions.fetchAPIreset();
setApiFlag({ itm: true, fvl: true });
}
}, [favList]);
return (<> </>);
};
GetItemsNFavlist.propTypes = {
user: PropTypes.objectOf(PropTypes.any).isRequired,
actions: PropTypes.objectOf(PropTypes.any).isRequired,
items: PropTypes.arrayOf(PropTypes.any).isRequired,
favList: PropTypes.arrayOf(PropTypes.any).isRequired,
fetchCall: PropTypes.objectOf(PropTypes.any).isRequired,
};
const mapStateToProps = ({
user, items, fetchCall, favList,
}) => ({
user, items, fetchCall, favList,
});
function mapActionsToProps(dispatch) {
return {
actions: bindActionCreators({ ...MyActions }, dispatch),
};
}
export default connect(mapStateToProps, mapActionsToProps)(GetItemsNFavlist);
And these are my actions (actions/index.js), where I have the API call function:
import axios from 'axios';
const addUsername = username => ({
type: 'SET_NAME',
username,
});
const setUserInfo = user => ({
type: 'SET_USER',
user,
});
const setItems = items => ({
type: 'SET_ITEMS',
items,
});
const setFavList = favList => ({
type: 'SET_FAVLIST',
favList,
});
const fetchAPIbegin = callHeader => ({
type: 'FETCH_API_BEGIN',
callHeader,
});
const fetchAPIsuccess = payload => ({
type: 'FETCH_API_SUCCESS',
payload,
});
const fetchAPIfailure = error => ({
type: 'FETCH_API_FAILURE',
payload: error,
});
const fetchAPIsuccesResp = payload => ({
type: 'FETCH_API_SUCCESS_RESP',
payload,
});
function handleErrors(response) {
if (!response.ok && response.error) { throw Error(JSON.stringify(response)); }
return response;
}
function fetchAPIcall(url, restAct, options) {
return dispatch => {
dispatch(fetchAPIbegin(url, options));
setTimeout(() => axios[restAct](url, options)
.then(handleErrors)
.then(rsp => {
dispatch(fetchAPIsuccesResp(rsp));
return rsp;
})
.then(resp => resp.data)
.then(jsonResp => dispatch(fetchAPIsuccess(jsonResp)))
.catch(err => dispatch(fetchAPIfailure(`${err}`))), 1000);
};
}
const fetchAPIreset = () => ({ type: 'FETCH_API_RESET' });
export {
addUsername,
setUserInfo,
setItems,
setFavList,
fetchAPIcall,
fetchAPIbegin,
fetchAPIsuccess,
fetchAPIfailure,
fetchAPIreset,
fetchAPIsuccesResp,
};
And Just in case, this is the link to my repo: find-my-item repo.
Thanks in advance!!
Best regards
import React from "react";
import { UserContext } from "./../contexts";
import {
removeStoredAuthData,
storedAuthIsValid,
storeNewAuthData,
} from "./../utils/auth";
import { getUserInfos } from "./../api/userAuthentication";
class UserProvider extends React.Component {
constructor(props) {
super(props);
this.state = {
user: "",
};
}
render() {
return (
<UserContext.Provider
value={{
user: this.state.user,
clearUserProfile: () => {
const user = "";
removeStoredAuthData();
this.setState({ user });
},
saveUserProfile: (response) => {
const user = response.data;
storeNewAuthData(response);
this.setState({ user });
},
populateUserProfile: (displayLoader, hideLoader) => {
const storedToken = localStorage.getItem("appsante-token");
const storedId = localStorage.getItem("appsante-id");
if (storedAuthIsValid()) {
displayLoader(() => {
getUserInfos(storedId)
.then((response) => {
const user = { ...response.data, token: storedToken };
this.setState({ user }, hideLoader());
})
.catch((error) => console.log(error));
});
}
},
}}
>
{this.props.children}
</UserContext.Provider>
);
}
}
export default UserProvider;
Hi everyone !
I trying to convert a React class component into a function component, with hooks.
But I can't find a way to deal properly with that line :
this.setState({ user }, hideLoader());
Unlike setState in class components, useState doesn't take a callback as second parameter, and I can't find how to achieve it with useEffect.
Could anyone help me ? Thanks !
Because the loader's presence can't be determined from the value in / change in user alone, you'll need another state variable, maybe one that contains the callback - perhaps call it hideLoader. After getUserInfos resolves, call setHideLoader with the callback, so that a useEffect hook with that function as a dependency can see the change and call the callback:
const [hideLoader, setHideLoader] = useState();
useEffect(() => {
if (hideLoader) {
hideLoader(); // or, if this is a HOF: hideLoader()()
setHideLoader(); // callback done; remove callback from state
}
}, [hideLoader]);
// ...
populateUserProfile: (displayLoader, hideLoaderParam) => {
// ...
getUserInfos(storedId)
.then((response) => {
setUser({ ...response.data, token: storedToken }); // hook version
setHideLoader(hideLoaderParam);
})
and the rest of your code can be mostly the same - only call setHideLoader up above, inside getUserInfos.
I think you should do this :-
import React, { useState } from 'react';
const [user, setUser] = useState("");
populateUserProfile: async (displayLoader, hideLoader) => {
const storedToken = localStorage.getItem("appsante-token");
const storedId = localStorage.getItem("appsante-id");
if (storedAuthIsValid()) {
displayLoader();
let response = await getUserInfos(storedId)
const user = { ...response.data, token: storedToken };
setUser(user);
hideLoader();
};
}
In the last couple of days I have been working on my Redux api call. I am actually having a problem getting the data back to the view component. Currently I'm able to see the data in the in the action generator, so I know at least I'm able to get it. However, nothing is showing in the view. I imagine it may have something to do with when it's loading. This is why I tried to load it when the component is rendering.
https://djangoandreact.herokuapp.com/user/1 is what is not loading.
codesandbox: https://codesandbox.io/s/zlor60q3jm?from-embed
Should be able to go to /user/1 at the end similar to going to /1 brings up an article(Tough Hope)
Heres the view component:
import React from "react";
import { connect } from "react-redux";
import { fetchUser } from "../store/actions/userActions";
class UserDetailView extends React.Component {
componentDidMount() {
const userID = this.props.match.params.userID;
fetchUser(userID); //fixed
}
render() {
const { user } = this.props.user;
console.log(user);
return (
<div>
<h3>{user.username}</h3>
</div>
);
}
}
const mapStateToProps = state => ({
user: state.user
});
const mapDispatchToProps = (dispatch, ownProps) => ({
fetchUser: dispatch(fetchUser(ownProps.match.params.userID))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserDetailView);
Action generator
import axios from "axios";
import { thunk } from "react-redux";
export function fetchUser(userID) {
console.log(userID);
return dispatch => {
return axios.get(`/api/user/${userID}`).then(res => {
dispatch(fetchUserSuccess(res.data));
console.log(res.data); // loads data
});
};
}
// Handle HTTP errors since fetch won't.
function handleErrors(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
export const FETCH_USER_BEGIN = "FETCH_USER_BEGIN";
export const FETCH_USER_SUCCESS = "FETCH_USER_SUCCESS";
export const FETCH_USER_FAILURE = "FETCH_USER_FAILURE";
export const fetchUserBegin = () => ({
type: FETCH_USER_BEGIN
});
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: { user }
});
export const fetchUserFailure = error => ({
type: FETCH_USER_FAILURE,
payload: { error }
});
Reducers(which are probably fine):
import {
FETCH_USER_BEGIN,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "../actions/actionTypes";
const initialState = {
user: {},
loading: false,
error: null
};
export default function userReducer(state = initialState, action) {
switch (action.type) {
case FETCH_USER_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
case FETCH_USER_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
user: {}
};
default:
return state;
}
}
folks. I found it.
case FETCH_USER_SUCCESS:
return {
...state,
loading: false,
user: action.payload.user
};
user is supposed to be user:action.payload
Also, the user action was supposed to be
export const fetchUserSuccess = user => ({
type: FETCH_USER_SUCCESS,
payload: user
})
WOOOOW. But, honestly, I learned so much about Redux in the last two sleepless nights, it was worth the pain. Really was. Now, instead of copy pasta, I know what an action generator is and does, and reducer (obvi)
In my store.js i have the following code:
import { createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk'
const reducer = (state, action) => {
console.log(action.type)
if (action.type === 'LOAD_USERS') {
return {
...state,
users: action.users['users']
}
} else if (action.type === 'LOAD_CHATROOMS') {
return {
...state,
chatRooms: action.chatRooms['chatRooms']
}
}
return state;
}
export default createStore(reducer, {users:[], chatRooms:[]}, applyMiddleware(thunk));
the code inside the action.type === 'LOAD_CHATROOMS' is never accessed for some reason, this is the action file where i set the action type for the reducer:
import axios from 'axios'
axios.defaults.withCredentials = true
const loadUsers = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_USERS',
users: response.data
});
});
};
};
const logIn = user => {
return axios.post('http://localhost:3000/session', {
user_id: user.id
})
.then(response => {
//TODO do something more relevant
console.log('loged in');
});
};
const loadChatRooms = () => {
return dispatch => {
return axios.get('http://localhost:3000/session/new.json')
.then(response => {
dispatch({
type: 'LOAD_CHATROOMS',
chatRooms: response.data
});
});
};
};
const enterChatRoom = chatrom => {
};
export { loadUsers, logIn, enterChatRoom, loadChatRooms};
The 'Load methods' get the data that i use to populate both components (one for users list and the other one for chatrooms list ), both components are called at the same level in the app.js file.
Basically the output that i'm getting is the first component (users) as expected with the correct list, and the chatrooms component is also rendered but the data is not loaded (since it's corresponding reducer block is not accessed).
Thanks a lot for reading :)