My dispatch for changing the state in redux is not called? - reactjs

I am trying to display flash message to the user on the login component after reseting the password. I 've commented axios calls because it's unimportant for this case. I am calling dispatch twice, first to set the state(success msg) and second time to set success to empty string.
This is my resetPassword action where i am calling dispatches:
export const resetPassword = values => async dispatch => {
try {
const token = window.location.href.split("/")[4];
const data = {
password: values.password,
confirmPassword: values.confirmPassword,
token
};
// let res = await axios.post(API_URL + "/resetuserpassword", data);
// console.log("resStatus:", res);
window.location.href = "http://localhost:3000/login";
dispatch({
type: RESET_SUCCESS,
payload:
"You successfully reset the password , just log in with the new one."
});
await sleep(2000);
dispatch({
type: RESET_SUCCESS,
payload: ""
});
catch (error) {
console.log("error occured:", error);
My ResetPassReducer :
import { RESET_SUCCESS } from "../actions/types";
export default (state = { success: "" }, action) => {
switch (action.type) {
case RESET_SUCCESS:
console.log("RESET_SUCCESS DISPATCHED...");
return {
success: action.payload
};
default:
return state;
}
};
and my renderMessage func in Login component:
renderMessage = () => {
const error = this.props.error;
const success = this.props.success;
if (success) {
return (
<FlashMessage duration={5000} style="color">
<p style={{ color: "green" }}> {success.toString()} </p>
</FlashMessage>
);
}
return null;
};

You are navigating away before making the dispatch calls. All the code located after the window.location.href = '...' won't be executed.
Just move window.location.href = "http://localhost:3000/login"; to the end of your block.

Related

State updates but not in component

I am using redux-toolkit and I was trying to handle request errors. after trying console loging I found out that my error state updates but not when I need.
const ProductCreatePage = () => {
const {token} = useContext(UserContext);
const {error} = useSelector(state => state.ui)
const dispatch = useDispatch()
const navigate = useNavigate();
const createProductHandler = async (event) => {
event.preventDefault();
const form = document.querySelector('form');
const productData = {
price: Number.parseInt(event.target.price.value),
name: event.target.name.value,
status: event.target.status.value === "true" ? true : false
};
const formData = new FormData();
event.target.querySelectorAll("input").forEach(({ name, type, value, files, ...element }) => {
if (type === 'file') {
formData.append(`files.img`, files[0], files[0].name);
}
});
formData.append('data', JSON.stringify(productData));
await dispatch(createProduct({
productData: formData,
token
}))
console.log(error)
if(error === false){
// navigate(routes.products,{state:{create: true}})
console.log("sss")
}
}
return(...)
}
this is the function that creates product in redux using redux-toolkit
export const createProduct = ({productData,token}) => {
return async (dispatch) => {
try {
dispatch(ProductSliceAction.loadingToggle())
const {data} = await axios.post(`https://www.youtube.com/watch?v=xWpnTGmS8-Q`,productData,{
headers: {
Authorization: `Bearer ${token}`
}
})
dispatch(UiSliceAction.resetErrors())
dispatch(ProductSliceAction.loadingToggle())
}catch (error){
dispatch(UiSliceAction.setErrors({
message: error.message,
code: error.response.status
}))
dispatch(ProductSliceAction.loadingToggle())
}
}
}
and this is my error redux slice
const initialState = {
error: false
}
const UiSlice = createSlice({
name: "ui",
initialState,
reducers: {
setErrors: (state,{payload}) => {
state.error = payload;
},
resetErrors: (state) => {
state.error = initialState.error;
}
}
});
I want to handle errors like "Network Error" , 403 , ... and store the error in UiSlice error and for doing that I am using dispatch like below
dispatch(UiSliceAction.setErrors({
message: error.message,
code: error.response.status
}))
the state updates but this update not effecting code below
if(error === false){
// navigate(routes.products,{state:{create: true}})
console.log("sss")
}
I did multiple console.log and I found out state does not update in component (it updates in reducer when use console.log)
now I want to know where is the problem. why my state updates with delay(after exciting if(error === false))
this is the logs
enter image description here

Changing Parent State with Arrow Function Inside a Function

I have a Register User Function Which Looks Like this:
onRegisterUser = () => {
const { email, password, isLoading} = this.state;
const { navigation } = this.props;
registerUser(
email,
password,
() =>
this.setState({
isLoading: !this.state.isLoading,
}),
navigation
);
};
The Function Receives the Input email, pass and isLoading state from the Register Screen and does the following:
import { Alert } from "react-native";
import firebase from "./firebase";
import { newUser } from "./database";
export const registerUser = (email, password, toggleLoading) => {
toggleLoading();
const isInputBlank = !email || !password;
if (isInputBlank) {
Alert.alert("Enter details to signup!");
toggleLoading();
}
//If Everything OK Register User
else {
//CR: change to async-await
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(() => {
newUser(firebase.auth().currentUser.uid);
})
.catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == "auth/weak-password") {
alert("The password is too weak.");
} else if (errorCode == "auth/invalid-email") {
alert("Email is Invalid");
} else if (errorCode == "auth/email-already-in-use") {
alert("Email is Already in use!");
} else {
alert(errorMessage);
}
console.log(error);
});
}
};
My problem is that the toggleLoading(); Inside if (isInputBlank) doesn't do anything
I'm trying to change the isLoading state if I get an error (Empty Input in this Example) but it does nothing,
It works only one time in the start and that's it.
If the Alert is Activated when i close it the loading screen Remains
What Am I missing?
Try this on your set loading function
() =>
this.setState((prevState) => ({
isLoading: !prevState.isLoading
})),
should it not be better to chain to the original promise like so:
export const registerUser = (email, password) => {
if (!email && ! password) {
return Promise.reject('Email and Password required'); // or whatever message you like to display
}
return (
yourCallToFirebase()
.then(() => newUser())
.catch(() => {
let errorMessage;
// your error handling logic
return Promise.reject(errorMessage);
})
)
};
usage
onRegisterUser = () => {
const { email, password, isLoading} = this.state;
const { navigation } = this.props;
this.setState({ isLoading: true })
registerUser(email,password)
.then(() => {
// your logic when user gets authenticated (ex. navigate to a route)
})
.catch((errorMessage) => {
// display feedback (like a toast)
})
.finall(() => this.setState({ isLoading: false }));
};

React dispatch not working (userService function not triggered in userAction)

When I submit my form, it triggers an action login (from userActions). In this action, I use dispatch to use my userService which makes an API call.
When I submit it, the dispatch is not working. If I console.log the result of the action I have my code that appears, like this:
Action was called // Custom message
dispatch => {
dispatch(request({
email
}))
_services_userService__WEBPACK_IMPORTED_MODULE_1__["userService"].login(email, password).then( appSate => {return appSate;},error => {console.lo…
I am supposed to retrieve my user... What is wrong here ?
LoginForm.js
handleFormSubmit(e) {
e.preventDefault();
const credentials = {
email: this.state.email,
password: this.state.password
}
if (credentials) {
let test = login(credentials);
console.log("Action was called");
console.log(test);
this.setState(redirect => true)
}
}
userActions.js -> login()
export const login = (email,password) => {
console.log('is in action');
return dispatch => {
dispatch(request({ email }));
userService.login(email,password)
.then(
appSate => {
return appSate;
},
error => {
console.log(error);
}
);
};
function request(user) { return { type: userConstants.LOGIN_REQUEST,user } }
}
userService.js -> login()
function login(credentials) {
console.log("In userService login function");
return axios.post('/api/login',credentials)
.then(response => {
if (response.data.success) {
console.log("Login Successful!");
let userData = {
firstname: response.data.user.firstname,
surname: response.data.user.surname,
id: response.data.user.id,
email: response.data.user.email,
auth_token: response.data.access_token,
};
let appState = {
isLoggedIn: true,
user: userData
};
localStorage.setItem("appState",JSON.stringify(appState));
return appState;
}
});
}
I think you forgot return statement userActions.js. Try this
export const login = (email,password) => {
console.log('is in action');
return dispatch => {
dispatch(request({ email }));
return userService.login(email,password)
.then(
appSate => {
return appSate;
},
error => {
console.log(error);
}
);
};
function request(user) { return { type: userConstants.LOGIN_REQUEST,user } }
}

How to Create Middleware for refresh token in Reactjs with axios and redux

i am working with reactjs on front end the issue is after certain time period the accessToken is expired and server send status of 401(unauthorized) then i need to send refresh token back to server it works fine until i manually send the refresh token i set the setInterval function but thats not a good approach how to automatically send it when token is expired.
i also google it but everyone is talking about creating middleware anyone please give me the hint how to create that middleware or any other solution or link any article related to it . i created this but this didnt works for me however when server send status of 401 then middleware ran but it dosent dispatch my refreshToken() function
const customMiddleWare = store => next => action => {
axios.interceptors.response.use(function (response) {
return response;
}, function (error) {
if(error.status === 401) {
// do something when unauthorized
store.dispatch(refreshToken());
}
return Promise.reject(error);
});
console.log("Middleware triggered:", action);
next(action);
}
By the way i am using redux, redux-thunk and axios. thanks,
some time ago i used to use the next way:
First of all i created some api folder, where each function returns data for axios requests
// /api.js
export function signIn (data) {
return {
method: 'post',
api: '/sign-in'
data: data
}
}
export function signUp (data) {
return {
method: 'post',
api: '/registration'
data: data
}
}
then i generated action type by specific rule, like: SIN_IN_REQUEST, where: SIGN_IN means signIn function in /api.js; REQUEST means that you need to do api request. As result my middleware looked like the next:
// request middleware
const instance = axios.create({
baseURL: '/api'
});
function camelize(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(word, index) {
return index === 0 ? word.toLowerCase() : word.toUpperCase();
}).replace(/\s+/g, '');
}
const customMiddleWare = store => next => action => {
if (!action.type.endsWith('_REQUEST')) {
next();
return;
}
const methodName = action.type.replace('_REQUEST', ''); // removed _REQUEST from action type
const camelCaseMethodName = camelize(methodName); // the result is "signIn"
const method = api[camelCaseMethodName];
if (!method) {
next();
return;
}
const dataForRequest = method(action.payload);
try {
const response = await instance(dataForRequest);
const newActionType = action.type.replace('_REQUEST', '_SUCCESS');
dispatch({
type: newActionType,
payload: {
requestPayload: action.payload,
response: response,
}
})
} catch(error) {
if (error.status === '401') {
dispatch(refreshToken());
next();
return;
}
const newActionType = action.type.replace('_REQUEST', '_FAILURE');
dispatch({
type: newActionType,
payload: {
requestPayload: action.payload,
error: error,
}
})
}
next();
}
After that you can easily manage any api request in your application like that:
function someTHunkMethod(username, password) {
return (dispatch, getState) => {
dispatch({
type: 'SIGN_IN_REQUEST',
payload: {
username,
password
}
})
}
}
function oneMoreThunk(data) {
return (dispatch, getState) => {
dispatch({
type: 'GET_USERS_REQUEST',
payload: data
})
}
}
And in reducer do something like that
...
switch (action.type) {
case 'SIGN_REQUEST':
return {
isLoading: true,
user: null
}
case 'SIGN_SUCCESS':
return {
isLoading: false,
user: action.payload.response.data
}
case 'SIGN_FAILURE':
return {
isLoading: false,
user: null
}
default:
return state
}

how to async/await redux-thunk actions?

action.js
export function getLoginStatus() {
return async(dispatch) => {
let token = await getOAuthToken();
let success = await verifyToken(token);
if (success == true) {
dispatch(loginStatus(success));
} else {
console.log("Success: False");
console.log("Token mismatch");
}
return success;
}
}
component.js
componentDidMount() {
this.props.dispatch(splashAction.getLoginStatus())
.then((success) => {
if (success == true) {
Actions.counter()
} else {
console.log("Login not successfull");
}
});
}
However, when I write component.js code with async/await like below I get this error:
Possible Unhandled Promise Rejection (id: 0): undefined is not a function (evaluating 'this.props.dispatch(splashAction.getLoginStatus())')
component.js
async componentDidMount() {
let success = await this.props.dispatch(splashAction.getLoginStatus());
if (success == true) {
Actions.counter()
} else {
console.log("Login not successfull");
}
}
How do I await a getLoginStatus() and then execute the rest of the statements?
Everything works quite well when using .then(). I doubt something is missing in my async/await implementation. trying to figure that out.
The Promise approach
export default function createUser(params) {
const request = axios.post('http://www...', params);
return (dispatch) => {
function onSuccess(success) {
dispatch({ type: CREATE_USER, payload: success });
return success;
}
function onError(error) {
dispatch({ type: ERROR_GENERATED, error });
return error;
}
request.then(success => onSuccess, error => onError);
};
}
The async/await approach
export default function createUser(params) {
return async dispatch => {
function onSuccess(success) {
dispatch({ type: CREATE_USER, payload: success });
return success;
}
function onError(error) {
dispatch({ type: ERROR_GENERATED, error });
return error;
}
try {
const success = await axios.post('http://www...', params);
return onSuccess(success);
} catch (error) {
return onError(error);
}
}
}
Referenced from the Medium post explaining Redux with async/await: https://medium.com/#kkomaz/react-to-async-await-553c43f243e2
Remixing Aspen's answer.
import axios from 'axios'
import * as types from './types'
export function fetchUsers () {
return async dispatch => {
try {
const users = await axios
.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => res.data)
dispatch({
type: types.FETCH_USERS,
payload: users,
})
} catch (err) {
dispatch({
type: types.UPDATE_ERRORS,
payload: [
{
code: 735,
message: err.message,
},
],
})
}
}
}
import * as types from '../actions/types'
const initialErrorsState = []
export default (state = initialErrorsState, { type, payload }) => {
switch (type) {
case types.UPDATE_ERRORS:
return payload.map(error => {
return {
code: error.code,
message: error.message,
}
})
default:
return state
}
}
This will allow you to specify an array of errors unique to an action.
Another remix for async await redux/thunk. I just find this a bit more maintainable and readable when coding a Thunk (a function that wraps an expression to delay its evaluation ~ redux-thunk )
actions.js
import axios from 'axios'
export const FETCHING_DATA = 'FETCHING_DATA'
export const SET_SOME_DATA = 'SET_SOME_DATA'
export const myAction = url => {
return dispatch => {
dispatch({
type: FETCHING_DATA,
fetching: true
})
getSomeAsyncData(dispatch, url)
}
}
async function getSomeAsyncData(dispatch, url) {
try {
const data = await axios.get(url).then(res => res.data)
dispatch({
type: SET_SOME_DATA,
data: data
})
} catch (err) {
dispatch({
type: SET_SOME_DATA,
data: null
})
}
dispatch({
type: FETCHING_DATA,
fetching: false
})
}
reducers.js
import { FETCHING_DATA, SET_SOME_DATA } from './actions'
export const fetching = (state = null, action) => {
switch (action.type) {
case FETCHING_DATA:
return action.fetching
default:
return state
}
}
export const data = (state = null, action) => {
switch (action.type) {
case SET_SOME_DATA:
return action.data
default:
return state
}
}
Possible Unhandled Promise Rejection
Seems like you're missing the .catch(error => {}); on your promise. Try this:
componentDidMount() {
this.props.dispatch(splashAction.getLoginStatus())
.then((success) => {
if (success == true) {
Actions.counter()
} else {
console.log("Login not successfull");
}
})
.catch(err => {
console.error(err.getMessage());
}) ;
}
use dispatch(this.props.splashAction.getLoginStatus()) instead this.props.dispatch(splashAction.getLoginStatus())

Resources