Custom error message for firebase for react and redux - reactjs

I am trying to create a Login feature using firebase and redux. I try to implement a custom error message when the user input the wrong credentials like so:
// Log in
export const signin = (data, onError) => {
console.log(data.email, data.password)
return async dispatch => {
try {
const res = await firebase.auth().signInWithEmailAndPassword(data.email, data.password)
.then(async () => {
if(res.user){
const user = await firebase.firestore().collection('users').doc(res.user.uid).get();
if(user.exists) {
const userData = user.data() as User;
dispatch({
type: SET_USER,
payload: userData
})
}
return res
}
}).catch((error) => {
switch(error.code) {
case 'auth/user-not-found ':
error.message = "User not found"
break;
}
})
}
catch (err) {
console.log(err);
onError();
dispatch(setError(err.message));
}
}
}
However, after I run my application, the application stuck in a loading loop forever. Can someone help me out? Thank you in advance. Cheers

Related

Can you change user.challengeName in Amplify

I'm trying to give the end-user an option on the UI to reset MFA if the end-user loses access to the device they've been using.
I want to change the user.challangeName response from "SOFTWARE_TOKEN_MFA" to "MFA_SETUP". Is this something that can be done? Can I change the challangeName through UI?
Code here
const login = async (email, password) => {
try {
const user = await Auth.signIn(email, password);
if (user) {
if (user.challengeName === 'MFA_SETUP') {
dispatch({
type: AUTH_RESULT_USER,
payload: user
});
navigate('/auth-login');
} else if (user.challengeName === 'SOFTWARE_TOKEN_MFA') {
dispatch({
type: AUTH_RESULT_USER,
payload: user
});
navigate('/auth-post-login');
} else if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
const attr = user.challengeParam?.userAttributes || null;
if (attr) {
dispatch({
type: AUTH_RESULT_USER,
payload: user
});
}
// console.log('before', user);
navigate('/set-password');
} else {
getUserDetails(user, user.signInUserSession.idToken.jwtToken);
}
}
} catch (e) {
console.log('error', e);
// await logout();
throw e;
}
};

React native data are getting in formvalues but while .post it got failed

In console of formvalues values are coming but while sending its not getting in response of console..its become status : 400 and field is required showing but same piece of code is done in expo nd working fine but in react native cli its not working please let me know how can i solve this issue...
export const login = (formValues, actions) => {
return async dispatch => {
dispatch(startSubmitting());
const url = `/auth/login`;
const formdata = new FormData();
formdata.append('email', formValues.email);
formdata.append('password', formValues.password);
console.log(formValues,'formValues');
const response = await api
.post(url, formdata)
.then(res => {
console.log(res,'res');
return res;
})
.catch(error => {
actions.setErrors(error.response.data.error);
console.log(error.response.data.error);
return error.response;
});
dispatch({
type: 'LOGIN',
payload: response,
});
dispatch(stopSubmitting());
if (response && response.data && response.data.access_token) {
await AsyncStorage.setItem('userToken', response.data.access_token,2);
}
};
};

Call a function after react-redux dispatch has finished

So I have here a form which which when submitted, calls this function:
const handleSubmit = (e) => {
e.preventDefault();
try {
dispatch(addCategory({ category, identifier, definition }));
} catch (err) {
console.log(err.message);
}
};
and this is the code to my addCategory action in a separate file.
export const addCategory =
({ category, identifier, definition }) =>
async (dispatch) => {
try {
const { data } = await axios.post(
"http://localhost:5000/Admin/addCategory",
{ category, identifier, definition }
);
dispatch({ type: "ADD_CATEG", payload: data });
} catch (error) {
dispatch({
type: "GET_ERROR",
payload: error.response.data.errorMessage,
});
}
};
As you can see, I have an error handling in my backend and it triggers when the user inputs a duplicate data. The way I am getting that error is through this:
const { error } = useSelector((state) => state.categories);
What I want is that after I dispatch my action in my handleSubmit, it checks whether the error is empty or not. I've tried to call a function after the try-catch block in handleSubmit which looks like this. I've tried to run it then I've entered correct inputs of data without error, it displays 'no err' but if I tried to enter a duplicate input, it doesn't give me an error but in my redux console, it is already there. When I submit the form again, then it will now display the 'with err'. I've been trying to figure it out but can't seem to find any solutions.
const try = () => {
if (error !== null) {
console.log("with err");
} else if (error === null) {
console.log("no err");
}
};
I've ran into this problem as well.
One is catching an error on the request being sent
The other is catching an error from data handling on the Redux side
This is a pattern I've used before, you just need to make sure you are handling errors at every level.
export const addCategory =
({ category, identifier, definition }) =>
async (dispatch) => {
let response
try {
response = await axios.post(
"http://localhost:5000/Admin/addCategory",
{ category, identifier, definition }
);
dispatch({ type: "ADD_CATEG", payload: data });
} catch (error) {
response =
dispatch({
type: "GET_ERROR",
payload: error.response.data.errorMessage,
});
}
if(response?.data){
// do stuff for success
}
// do stuff for errors
};

What's causing this async function to work only after a page refresh?

I've been attempting to find discussions about this for over a week now, but most issues seem related to trouble persisting through a refresh, while I'm having state troubles without refreshing, so I'm not getting much of anywhere with it.
I'm attempting to load a gallery of images after a user logs in. The login is functioning properly--updates the state with a reducer and pushes from /login to /gallery and I can see in the inspector that the user ID updates from null to a value.
At /gallery I attempt to retrieve some data through axios asynchronously. It's a POST request so that I can send the user's ID in the body rather than the url/using params.
On initial login, state.images doesn't update and throws this error:
"data: "Cast to string failed for value "{ user: '60fc726d827a4e3daff47619' }" (type Object) at path "user" for model "Upload"" " Relative to my database/models: I've tried adjusting type on the model, both the $type approach and adjusting for a String that is an array, the former caused errors large enough for the page not to load, the latter affected no discernible changes.
If I reload the page, everything works and the images load. If I click from gallery to home and then back to gallery again, nothing changes in the state. I have no idea if this is an issue with my amateur async function structure, my mongodb setup, my reducer, the axios post itself, or something else entirely.
I've read that pretty much everything needs to be lined up inside of useEffect() but I've had absolutely no luck getting that to function either.
The whole of the code is at https://github.com/polysnacktyl/react-foraging, but here's the (seemingly) most relevant:
Login
function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const { getLoggedIn } = useContext(AuthContext);
const { dispatch } = useContext(Context);
const history = useHistory();
async function login(e) {
e.preventDefault();
try {
const { data } = await axios.post('http://localhost:3000/auth/login', {
email,
password
});
await getLoggedIn();
dispatch({
type: 'login',
payload: { user: data._id }
})
window.localStorage.setItem('user', JSON.stringify(data._id))
history.push('/gallery');
} catch (err) {
console.error(err);
}
}
return (... and so forth
Image Gallery
const { state, dispatch } = useContext(Context);
const [isLoading, setLoading] = useState(true);
const [images, setImages] = useState({ images: [] });
const user = state.user;
const success = async () => {
try {
const res = await axios.post('http://localhost:3000/auth/mine', { user });
dispatch({
type: 'fetchSuccess',
payload: { images: res.data }
})
setImages(res.data);
} catch (err) { console.log(err.response) }
}
const fail = (error) =>
dispatch({
type: 'fetchFail',
payload: { error: error.message }
});
function loadImages() {
dispatch({ type: 'fetchImages' });
setTimeout(async () => {
try {
await success();
setLoading(false)
} catch (error) {
await fail(error);
}
}, 1000);
}
useEffect(() => {
loadImages()
//eslint-disable-next-line
}, [])
if (isLoading) {
return (<div className='loading'>...loading</div>)
} else {
return (...and so on
Let's have a look at your loadImages. This function is called during componentDidMount. This seems OK on first glance but it is a bug actually.
loadImages internally calls success that actually depends on user variable.
Having this in mind what we can do is following:
useEffect(() => {
if(!user) { // if no user present somehow, let's return
return;
}
loadImages() // load the images
//eslint-disable-next-line
}, [user]) // add a dependency to the user, since we load user images actually
I think with this approach the issue will be fixed and also you can remove the setTimeout in loadImages in my opinion.
As it turns out, my issue was in my axios-post-with-req-body strategy. I switched it to a GET request and sent the user ID in the params and now everything is loading on initial login and persisting through page relaods. I'm still not sure why it was causing the behavior it was, but at least I know how to avoid it.
the setup that ultimately worked out for me:
function Gallery() {
const { state, dispatch } = useContext(Context);
const [isLoading, setLoading] = useState(true);
const [images, setImages] = useState([]);
const user = state.user;
const success = async () => {
const res = await axios.get('http://localhost:3000/auth/mine', {
params: { user }
})
dispatch({
type: 'fetchSuccess',
payload: res.data
})
setImages(res.data);
}
const fail = (error) =>
dispatch({
type: 'fetchFail',
payload: { error: error.message }
});
function loadImages() {
dispatch({ type: 'fetchImages' });
setTimeout(async () => {
try {
await success();
setLoading(false)
} catch (error) {
await fail(error);
}
}, 0);
}
useEffect(() => {
loadImages()
//eslint-disable-next-line
}, [])
if (isLoading) {
return (<div className='loading'>...loading</div>)
} else {
return (...images that actually load. wonderful.)
Additionally, I had to adjust the router to accept req.params.user instead of req.body.user.

Fetch array of url's

I'm working with redux and I am trying to fetch Star War API.
Here is my code:
import { MOVIES_ERROR, MOVIE_CHARACTERS } from "./types";
// Get all characters
export const getCharacters = (userId) => async (dispatch) => {
try {
const res = await fetch(`https://swapi.dev/api/films/${userId}`);
if (!res.ok) {
throw new Error("sometheing went wrong");
}
const getData = await res.json();
const characters = await getData.characters;
let people = [];
Promise.all(
characters.map((url) =>
fetch(url)
.then((response) => response.json())
.then((name) => people.push(name))
)
);
dispatch({
type: MOVIE_CHARACTERS,
payload: people,
});
} catch (err) {
dispatch({
type: MOVIES_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};
when I make a console log inside a promise. all I got people array filled with all the data, but when I dispatch it I got an empty array in the reducer. can anyone tell me what the mistake that i did?
I got the problem just now, I need to add await before Promise.all :)

Resources