I am trying to implement an account verification via the twilio api where it texts a user a code and it should set the state of generatedCode to that value, but i am having a difficult time getting that value of the dispatch. If i do a simple await with axios call it works fine, but not so much with a dispatch
action:
export const attemptRegisterVerify = (phone) => async (dispatch) => {
dispatch(registerVerifyBegin());
await postRegisterVerifyPhone(phone)
.then((res) => {
dispatch(registerVerifySuccess(res.data));
})
.catch((error) => {
dispatch(registerVerifyFail(error));
});
};
handleVerification:
const handleVerification = async (values) => {
const { email, phone } = values;
setModal(true);
try {
const response = await axios.post(
'http://localhost:8000/api/v1/auth/register/check-duplicate',
{
email,
phone,
}
);
if (response.data.success) {
switch (response.data.message) {
case 0:
try {
const response = await dispatch(
attemptRegisterVerify({ to: phone })
);
console.log(response);
// if (response.data.success) {
// setGeneratedCode(response.data.message);
// console.log(generatedCode);
// } else {
// console.log(response.data.message);
// }
} catch (error) {
console.log(error);
}
break;
case 1:
console.log('Email Address already in use.');
break;
case 2:
console.log('Phone number already in use.');
break;
case 3:
console.log('Email and Phone in use.');
break;
}
} else {
console.log(response.data.message);
}
} catch (error) {
console.log(error);
}
};
The console.log(response) returns undefined. Is there a way for it to wait until that dispatch is completed, then proceed?
export const attemptRegisterVerify = (phone) => async (dispatch) => {
dispatch(registerVerifyBegin());
await postRegisterVerifyPhone(phone)
.then((res) => {
dispatch(registerVerifySuccess(res.data));
return res.data; // return console.log(res.data) works
})
.catch((error) => {
dispatch(registerVerifyFail(error));
});
};
Dispatch returns what you return in the function call. In the case of an async thunk, it'll return Promise<void> by default unless you return something.
If in your attemptRegisterVerify thunk, if you add return 'test' to the end of it, then your console.log(response) would log 'test'.
In a non-thunk, the return value of the dispatch would be the action that was generated.
Related
nestjs controller.ts
#Patch(':id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
#Body('shippingAddr') addrShipping: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling, addrShipping);
return null;
}
nestjs service.ts
async updateProduct(
addressId: string,
addrBilling: boolean,
addrShipping: boolean,
) {
const updatedProduct = await this.findAddress(addressId);
if (addrBilling) {
updatedProduct.billingAddr = addrBilling;
}
if (addrShipping) {
updatedProduct.shippingAddr = addrShipping;
}
updatedProduct.save();
}
there is no problem here. I can patch in localhost:8000/address/addressid in postman and change billingAddr to true or false.the backend is working properly.
how can i call react with axios?
page.js
const ChangeBillingAddress = async (param,param2) => {
try {
await authService.setBilling(param,param2).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
return....
<Button size='sm' variant={data.billingAddr === true ? ("outline-secondary") : ("info")} onClick={() => ChangeBillingAddress (data._id,data.billingAddr)}>
auth.service.js
const setBilling = async (param,param2) => {
let adressid = `${param}`;
const url = `http://localhost:8001/address/`+ adressid ;
return axios.patch(url,param, param2).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
I have to make sure the parameters are the billlingddress field and change it to true.
I can't make any changes when react button click
Since patch method is working fine in postman, and server is also working fine, here's a tip for frontend debugging
Hard code url id and replace param with hard coded values too:
const setBilling = async (param,param2) => {
// let adressid = `${param}`;
const url = `http://localhost:8001/address/123`; // hard code a addressid
return axios.patch(url,param, param2).then((response) => { // hard code params too
console.log(response); // see console result
if (response.data.token) {
// localStorage.setItem("user", JSON.stringify(response.data));
}
// return response.data;
})
}
now it worked correctly
#Patch('/:id')
async updateProduct(
#Param('id') addrId: string,
#Body('billingAddr') addrBilling: boolean,
) {
await this.addrService.updateProduct(addrId, addrBilling);
return null;
}
const ChangeBillingAddress = async (param) => {
try {
await authService.setBilling(param,true).then(
() => {
window.location.reload();
},
(error) => {
console.log(error);
}
);
}
catch (err) {
console.log(err);
}
}
const setBilling= async (param,param2) => {
let id = `${param}`;
const url = `http://localhost:8001/address/`+ id;
return axios.patch(url,{billingAddr: param2}).then((response) => {
if (response.data.token) {
localStorage.setItem("user", JSON.stringify(response.data));
}
return response.data;
})
}
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 }));
};
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.
I'm calling an action from a component:
this.props.createWebsite(this.state)
This calls an action and passes in some state. The action looks like this:
export const createWebsite = data => {
return (dispatch, getState) => {
return axios.post(
API.path + 'website/',
{
// some data
}
)
.then(response => {
})
.catch(error => {
})
}
}
I want to handle the response and error in the component that called this, rather than in the action itself. How can I do this? I have tried:
this.props.createWebsite(this.state).then(response => { /**/ }).catch(error => { /**/ })
This sort of works but it doesn't catch errors.
You need to remove the catch from the createWebsite declaration.
It handle the error and to not propagate it. So the error is lost.
To get it :
remove the catch
export const createWebsite = data => {
return (dispatch, getState) => {
return axios.post(
API.path + 'website/',
{
// some data
}
)
.then(response => {
return response;
})
}
}
rethrow the exception
export const createWebsite = data => {
return (dispatch, getState) => {
return axios.post(
API.path + 'website/',
{
// some data
}
)
.then(response => {
return response;
})
.catch(error => {
// Do something
throw error;
})
}
}
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())