useState does not update the state within .catch after dispatching of asynk thunk - reactjs

In the following code setError does not update error in the catch after async thunk from redux-toolkit.
const [error, setError] = useState("");
const handleLogin = () => {
dispatch(userLogin({ email, password }))
.unwrap()
.then(() => {
if (userInfo) {
navigate("/profile");
}
})
.catch((err) => {
setError(err) // does not work
});
};
<button onClick={handleLogin}>Login</button>
export const userLogin = createAsyncThunk(
"auth/login",
async ({ email, password }, thunkAPI) => {
try {
const { data } = await userLoginRequest(email, password);
return data;
} catch (error) {
return thunkAPI.rejectWithValue(ERR_USER_LOGIN);
// ERR_USER_LOGIN is just a constant string from another file
}
}
);
I know that useState does not apply changed immediatly but in my case it ignores changes at all. I suppose that the problem can be related to the scope or something like this. So I've tried to use additional callback which I sent as a parameter and change the state through it but it also does not work.

Your userLogin function actually has caught the error, making handleLogin catch not catch anything.
You can throw the error within userLogin, so handleLogin can catch the error by itself.
export const userLogin = createAsyncThunk(
"auth/login",
async ({ email, password }, thunkAPI) => {
try {
const { data } = await userLoginRequest(email, password);
return data;
} catch (error) {
thunkAPI.rejectWithValue(ERR_USER_LOGIN);
// Add this
throw error;
}
}
);

Related

useEffect is re-rendering async promise again and again

I am trying to log currentUser details to console to see if user is logged in or not. But it is logging again n again and make a loop of currentUser in console.
Here is the code
const [currentUser, setCurrentUser] = useState(null);
const unSubscribeFromAuth = useRef(null);
useEffect(() => {
unSubscribeFromAuth.current = auth.onAuthStateChanged(async userAuth => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
userRef.onSnapshot(snapShot => {
setCurrentUser({
id: snapShot.id,
...snapShot.data()
})
console.log(currentUser);
})
}
else {
setCurrentUser(userAuth);
}
})
return () => { unSubscribeFromAuth.current() };
}, [currentUser]);
and here is the function i am importing
export const createUserProfileDocument = async (userAuth, additionalData) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = userRef.get();
console.log(snapShot);
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt,
...additionalData
})
}
catch (error) {
console.log('error creating user', error.message);
}
}
return userRef;
}
I tried to remove dependency but it says
React Hook useEffect has a missing dependency: 'currentUser'. Either include it or remove the dependency array
Since you are updating currentUser in the useEffect and the useEffect is triggered every time currentUser changes, you are seeing the infinite loop. You need to remove currentUser from the dependency array.
useEffect has a missing dependency is coming from console.log. If you remove it, it will go away.

TypeError: auth is not a function after logging in or signing up

I'm getting this error after trying to either sign up or login as a pop up
localhost:3000 says TypeError: _auth_base_js__WEBPACK_IMPORTED_MODULE_6__.default.auth is not a function
I'm not sure where the problem is coming from but if you need here is my login page
const Login = ({ history }) => {
const handleLogin = useCallback(
async (event) => {
event.preventDefault();
const { email, password } = event.target.elements;
try {
await app
.auth()
.signInWithEmailAndPassword(email.value, password.value);
history.push("/");
} catch (error) {
alert(error);
}
},
[history]
);
// var { currentUser } = useContext(AuthContext);
// if (currentUser) {
// return <Redirect to="/" />;
// }
and here is my signup page:
const SignUp = ({ history }) => {
const handleSignUp = useCallback(
async (event) => {
event.preventDefault();
const { email, password } = event.target.elements;
try {
await app
.auth()
.createUserWithEmailAndPassword(email.value, password.value);
history.push("/");
} catch (error) {
alert(error);
}
},
[history]
);
Sorry for asking too much but I have looked everywhere and tried every possible solution, but none worked for me.
i solved it by routing the files to the required pages

How do you destructure a function from RTK Query's generated useQuery hooks?

If logIn can be destructured from
const [logIn] = useLogInMutation();
and be used with
const handleLogin = async () => {
const username = 'username';
const password = 'password';
await logIn({
username,
password,
});
};
how do I perform a query similar to something like this
const handleGetUser = async () => {
await getUser();
};
from this
const { data: user, isSuccess, isError, error } = useGetUserQuery();
Export the useLazyQuery hook (useLazyGetUserQuery in this case)
export const {
useLogInMutation,
useGetUserQuery,
useLazyGetUserQuery,
useLogOutMutation,
} = authApi;
Import and use the hook
const [getUser] = useLazyGetUserQuery();
const handleGetUser = async () => {
try {
const user = await getUser().unwrap();
console.log(user);
} catch (err) {
if (err?.status === 401) {
console.error('Unautheticated.');
}
}
};
Thanks #Kapobajza

once the data has been fetched the state value are not getting updated

I'm trying to change the state value once the data has been fetched. I can see that the JSON has been fetched on the network tab but the state value hasn't been changed. State values are logged before the fetch request, I've added await but it hasn't been resolved yet. Do I've to use useEffect for a fetch request, I've tried to use useEffect but it triggers the request once I import this hook is there a workaround?
import axios from 'axios'
import { useState } from 'react'
export const useSignup = () => {
const [loading, setLoading] = useState(true)
const [status, setStatus] = useState(false)
const [msg, setMsg] = useState('')
const registerUser = async (emailAddress, password) => {
try {
await axios
.post('/signup', {
emailAddress: emailAddress,
password: password,
})
.then((res) => {
setStatus(res?.data.success)
setMsg(res?.data.msg)
})
.catch((err) => {
setStatus(err?.response.data.success)
setMsg(err?.response.data.msg)
})
} catch (err) {
console.error(err)
setStatus(false)
setMsg('Error Occured')
} finally {
console.log(msg, status)
setLoading(false)
}
}
return { loading, status, msg, registerUser }
}
You should trigger your function call via a useEffect hook.
Also, if you are using async/await you shouldn't mix it with a Promise-based approach.
Modify the custom hook to accept the two parameters, add the useEffect call and edit your registerUser function:
export const useSignup = (emailAddress, password) => {
const [loading, setLoading] = useState(true);
const [status, setStatus] = useState(false);
const [msg, setMsg] = useState('');
const registerUser = async (emailAddress, password) => {
try {
const { data } = await axios.post('/signup', { emailAddress, password })
setStatus(data.success)
setMsg(data.msg)
} catch (err) {
console.error(err);
setStatus(false);
setMsg('Error Occured');
}
};
useEffect(() => {
registerUser(emailAddress, password);
}, [])
return { loading, status, msg, registerUser };
};
Then you can call your useSignup hook like this
const { loading, status, msg, registerUser } = useSignup('username', 'password')

Prevent `useEffect` from looping

I have a simple useEffect that I'm not sure how to stop from invoking endlessly. It keeps firing the first if conditional endlessly. I've been reading a lot about hooks and I assume (maybe erroneously) that each render of the component results in a new invocation of my useAuth() and useUser() hooks. Since they have new references in memory it's triggering the useEffect's deps since technically it's a new function that exists in the scope of this new component render?
Thats my thought at least, no clue how to fix that if that's indeed that case.
const RootPage = ({ Component, pageProps }): JSX.Element => {
const { logoutUser } = useAuth(); // imported
const { fetchUser } = useUser(); // imported
const router = useRouter();
useEffect(() => {
// authStatus();
const unsubscribe = firebaseAuth.onAuthStateChanged((user) => {
if (user) {
console.log(1);
return fetchUser(user.uid); // async function that fetches from db and updates redux
}
console.log(2);
return logoutUser(); // clears userData in redux
});
return () => unsubscribe();
}, [fetchUser, logoutUser]);
...
}
fetchUser
const fetchUser = async (uid) => {
try {
// find user doc with matching id
const response = await firebaseFirestore
.collection('users')
.doc(uid)
.get();
const user = response.data();
// update redux with user
if (response) {
return dispatch({
type: FETCH_USER,
payload: user,
});
}
console.log('no user found');
} catch (error) {
console.error(error);
}
};
logoutUser
const logoutUser = async () => {
try {
// logout from firebase
await firebaseAuth.signOut();
// reset user state in redux
resetUser();
return;
} catch (error) {
console.error(error);
}
};
when I refresh the page with this useEffect on this is output to the console:
useEffect(() => {
function onAuthStateChange() {
return firebaseAuth.onAuthStateChanged((user) => {
if (user) {
fetchUser(user.uid);
} else {
resetUser();
}
});
}
const unsubscribe = onAuthStateChange();
return () => {
unsubscribe();
};
}, [fetchUser, resetUser]);
Keeping everything the same && wrapping fetchUser and resetUser with a useCallback, this solution seems to be working correctly. I'm not entirely sure why at the moment.

Resources