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

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

Related

Couldn't add current user after signing up without reloading (Next JS + Firebase)

I am using the Auth Provider to manage my Firebase auth information. I want to be able to use currentUser as soon as I sign up, but it won't set without reloading.
I tried to setCurrentUser out of the Auth Provider and set it, but I could not get it to work either.
contexts/Auth.tsx
const AuthContext = createContext<IAuthContext>(null!)
export const AuthProvider = ({
children,
}: {
children: ReactNode
}) => {
const [currentFBUser, setCurrentFBUser] = useState<firebase.User | null>(null)
const [currentUser, setCurrentUser] = useState<any>(null)
const [isLoading, setIsLoading] = useState<boolean>(true)
const { update } = useIntercom()
/**
* SUBSCRIBE user auth state from firebase
*/
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (!user) {
setCurrentFBUser(null)
setIsLoading(false)
return
}
await setCurrentFBUser(user)
const storeUser = await userRepository.findById(user.uid)
if (!storeUser) {
setCurrentUser(null)
setIsLoading(false)
return
}
await setCurrentUser(storeUser)
/* UPDATE Intercom props */
if(currentUser) {
update({
name: currentUser.name,
email: currentUser.email
})
}
setIsLoading(false)
return () => {
unsubscribe()
}
})
}, [])
const logout = useCallback(() => {
const auth = getAuth();
signOut(auth).then(() => {
window.location.reload()
}).catch((err) => {
toast.error(err.message)
});
}, [])
return (
<AuthContext.Provider value={{
currentFBUser,
currentUser,
setCurrentUser,
isLoading,
logout,
}}>
{children}
</AuthContext.Provider>
)
}
export const useAuthContext = () => {
const context = useContext(AuthContext)
if (!context) {
throw new Error('useAuth must be used within the AuthProvider')
}
return context
}
signup.tsx
...
const { currentFBUser, isLoading, setCurrentUser } = useAuthContext()
const signup = handleSubmit(
async (data) => {
if (data.password != data.confirmPassword) {
toast.error('Your password is not matched!')
return
}
const auth = getAuth()
createUserWithEmailAndPassword(auth, data.email, data.password)
.then((userCredential) => {
const auth = userCredential.user
if (!auth) return
const { email, uid } = auth
if (!email) return
const user = userRepository.findOrCreate(email, uid)
setCurrentUser(user)
})
.catch((err) => {
toast.error(err.message)
});
},
(err: any) => {
toast.error(err.message)
},
)
...
Try to call again getAuth() instead of using the response from createUserWithEmailAndPassword
const { currentFBUser, isLoading, setCurrentUser } = useAuthContext()
const signup = handleSubmit(
async (data) => {
if (data.password != data.confirmPassword) {
toast.error('Your password is not matched!')
return
}
const auth = getAuth()
createUserWithEmailAndPassword(auth, data.email, data.password)
.then((userCredential) => {
// const auth = userCredential.user
const auth = getAuth().currentUser
if (!auth) return
const { email, uid } = auth
if (!email) return
const user = userRepository.findOrCreate(email, uid)
setCurrentUser(user)
})
.catch((err) => {
toast.error(err.message)
});
},
(err: any) => {
toast.error(err.message)
},
)

How can I mock this function? Jest/ Jest&Enzyme

So, I tried to get an access to postForm function inside of the component, which uses with useForm() as a parameter.
I use jest&enzyme to test it.
const ResetPasswordForm: React.FC<Props> = ({ hideResetPassword }) => {
const router = useRouter()
const { loginAfterPasswordReset, resetPassword } = useAuth()
const [submitting, setSubmitting] = useState(false)
const postForm = async () => {
if (!isFormValid) return
try {
setSubmitting(true)
await resetPassword(values.password)
setSuccessMessage('Your password has been successfully updated')
} catch (error) {
handleErrorMessages(error)
} finally {
setSubmitting(false)
}
}
const login = (): void => {
try {
loginAfterPasswordReset()
const returnUrl = (router.query.returnUrl as string) || '/'
router.push(returnUrl)
} catch (error) {
handleErrorMessages(error)
}
}
const {
errorMessages,
handleErrorMessages,
onChange,
onSubmit,
submitErrorMessage,
successMessage,
setSuccessMessage,
values,
} = useForm(postForm, initialState)
What I tried to do:
it('should render ResetPasswordForm', () => {
component.postForm()
const instance = component.instance()
const spy = jest.spyOn(instance, 'postForm')
instance.forceUpdate();
expect(spy).toHaveBeenCalled()
})
How can I cover postForm function? Thanks

My Response from the database returns undefined

I am working on a login page and trying to store my data in context. But i get response from the database as undefined.
I think i am doing something wrong. Help please
Here's my code snippet
"AUTH CONTEXT" (The auth Context file returns undefined as response)
import { signIn as signInApi } from '../apis'
const AuthContext = createContext()
export const AuthProvider = ({ children }) => {
const [token, setToken] = useState(localStorage.getItem('token'))
const [user, setUser] = useState(localStorage.getItem('user'))
const [loading, setLoading] = useState(false)
const signIn = async (email, password, callback) => {
setLoading(true)
const res = await signInApi(email, password)
console.log(res)
......
const value = {
token,
loading,
signIn,
signOut,
}
}
export default AuthContext
APIS.JS (The API.js file below returns response data from the database)
import axios from 'axios'
export const signIn = async (email, password) => {
try {
const res = await axios.post(
`${process.env.REACT_APP_API}/auth/login`,
{
email,
password,
},
{
headers: {
'Content-Type': 'application/json',
},
}
)
} catch (error) {
console.log(error)
}
}
LOGIN FILE
const auth = useContext(AuthContext)
const handleLogin = (e) => {
e.preventDefault()
auth.signIn(email, password, () => history.replace('/admin'))
}
you didnot return response from your signIn function in API.js file

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')

Wait for useLazyQuery response

I need to call a query when submit button is pressed and then handle the response.
I need something like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(res) // handle response
}
Try #1:
const [checkEmail, { data }] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(data) // undefined the first time
}
Thanks in advance!
This works for me:
const { refetch } = useQuery(CHECK_EMAIL, {
skip: !values.email
})
const handleSubmit = async () => {
const res = await refetch({ variables: { email: values.email }})
console.log(res)
}
After all, this is my solution.
export function useLazyQuery<TData = any, TVariables = OperationVariables>(query: DocumentNode) {
const client = useApolloClient()
return React.useCallback(
(variables: TVariables) =>
client.query<TData, TVariables>({
query: query,
variables: variables,
}),
[client]
)
}
You could also use the onCompleted option of the useLazyQuery hook like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL, {
onCompleted: (data) => {
console.log(data);
}
});
const handleSubmit = () => {
checkEmail({ variables: { email: values.email }});
}
In case someone wants to fetch multiple apis at single load, it could be achieved like this.
On Demand Load > e.g. onClick, onChange
On Startup > e.g. useEffect
import { useLazyQuery } from "#apollo/client";
import { useState, useEffect } from "react";
import { GET_DOGS } from "../../utils/apiUtils";
const DisplayDogsLazy = () => {
const [getDogs] = useLazyQuery(GET_DOGS);
const [data, setData] = useState([]);
useEffect(() => {
getAllData();
}, []);
const getAllData = async () => {
const response = await getDogs();
console.log("Awaited response >", response);
};
const handleGetDogsClick = async () => {
const response = await getDogs();
setData(response.data.dogs);
};
return (
<>
<button onClick={handleGetDogsClick}>Get Dogs</button>
{data?.length > 0 && (
<ul>
{data?.map((dog) => (
<li key={dog.id} value={dog.breed}>
{dog.breed}
</li>
))}
</ul>
)}
</>
);
};
export default DisplayDogsLazy;

Resources