I don't know If I'm doing this the right way, it's the first time I've implemented GraphQL Apollo Client with Redux Toolkit.
So I have a Thunk:
import { createAsyncThunk } from "#reduxjs/toolkit";
import { useMutation } from "#apollo/client";
import {LOGIN_USER} from "../../graphql/mutations/auth";
export const Authenticate = createAsyncThunk(
"auth/login",
async (data, thunkAPI) => {
const {email, password} = data;
const [login, { loading, error, data: user }] = useMutation(LOGIN_USER,{
variables: {
email,
password
}
});
await login();
if (error) {
return thunkAPI.rejectWithValue(error);
}
console.log(user);
return user;
}
)
the mutation looks like this:
import {gql} from "graphql-tag";
export const LOGIN_USER = gql`
mutation login($email: String!, $password: String!) {
login(email: $email, password: $password) {
id
email
name
token
}
}
`;
However, In my component, when I dispatch the action, nothing happens, I've checked the network tab, but there are no requests to my apollo server.
import {Authenticate} from "../../features/user/userActions";
import {useDispatch, useSelector} from "react-redux";
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(Authenticate({email, password}));
};
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
I'd appreciate any provided help and support
Related
A user of my website encountered an error message while attempting to sign up using GitHub on my React site from his chrome browser(mobile). I have integrated Firebase GitHub sign-in with the popup method.
Unable to process request due to missing initial state. This may happen if browser sessionStorage is inaccessible or accidentally cleared.
Code for signup:
import { useEffect, useState } from "react"
import { GithubAuthProvider, signInWithPopup } from "firebase/auth"
import { auth } from "../firebase/config"
import { createUserProfileDocument } from "../firebase/createUserProfileDocument"
import { useAuthContext } from "./useAuthContext"
export const useSignup = () => {
const [error, setError] = useState(false)
const [isPending, setIsPending] = useState(false)
const [isCancelled, setIsCancelled] = useState(false)
const provider = new GithubAuthProvider()
const { dispatch } = useAuthContext()
const signup = async () => {
setError(null)
setIsPending(true)
try {
const res = await signInWithPopup(auth, provider)
if (!res) {
throw new Error("Could not complete signup")
}
const user = res.user
await createUserProfileDocument(user)
dispatch({ type: "LOGIN", payload: user })
if (!isCancelled) {
setIsPending(false)
setError(null)
}
} catch (error) {
if (!isCancelled) {
setError(error.message)
setIsPending(false)
}
}
}
useEffect(() => {
return () => setIsCancelled(true)
}, [])
return { signup, error, isPending }
}
useAuthContext code:
import { useContext } from "react"
import { AuthContext } from "../context/AuthContext"
export const useAuthContext = () => {
const context = useContext(AuthContext)
if (!context) {
throw Error("useAuthContext must be used inside an AuthContextProvider")
}
return context
}
AuthContext code
import { createContext, useEffect, useReducer } from "react"
import { onAuthStateChanged } from "firebase/auth"
import { auth } from "../firebase/config"
import { authReducer } from "../reducers/authReducer"
export const AuthContext = createContext()
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(authReducer, {
user: null,
authIsReady: false,
})
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
dispatch({ type: "AUTH_IS_READY", payload: user })
})
return unsubscribe
}, [])
return (
<AuthContext.Provider value={{ ...state, dispatch }}>{children}</AuthContext.Provider>
)
}
firebaseConfig object:
const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_APP_ID,
measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID,
}
initializeApp(firebaseConfig)
const db = getFirestore()
const auth = getAuth()
export { auth, db, logEvent }
I enabled the firestore cache by calling the enableIndexedDbPersistence method in my react app. here is my code:
import { initializeApp } from "firebase/app"
import { getAuth } from "firebase/auth"
import { enableIndexedDbPersistence, getFirestore } from "firebase/firestore"
const firebaseConfig = {
// config code
}
initializeApp(firebaseConfig)
const db = getFirestore()
enableIndexedDbPersistence(db) // enable cache
const auth = getAuth()
export { auth, db }
I would like to know how I can verify that I'm getting data from the cache, not from the server in the below code?
import { useEffect, useState } from "react"
import { collection, limit, onSnapshot, orderBy, query, where } from "firebase/firestore"
import { db } from "../firebase/config"
export const useCollection = (c) => {
const [documents, setDocuments] = useState([])
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState(null)
useEffect(() => {
let ref = collection(db, c)
const unsubscribe = onSnapshot(ref, (snapshot) => {
const results = []
console.log(snapshot.metadata.fromCache ? "local data" : "server data")
snapshot.docs.forEach(
(doc) => {
results.push({ ...doc.data(), id: doc.id })
},
(error) => {
console.log(error)
setError("could not fetch the data")
}
)
setDocuments(results)
setIsLoading(false)
setError(null)
})
return () => unsubscribe()
}, [])
return { documents, error, isLoading }
}
Is there anything apart from calling the enableIndexedDbPersistence method I need to do to configure the cache properly?
Will this method cache the subcollection documents as well?
I am using Redux/Toolkit and I want to use the Async Thunk for authentication processes. But it returns an error when I'm trying to dispatch the function.
What should I do in this case? It's the first time for me to use Async Thunk, so I don't have an idea how to face with this problem.
By the way I am using Typescript. So, I think this problem mostly about Typescript.
userSlice.tsx file:
import {createSlice, createAsyncThunk} from "#reduxjs/toolkit"
import {InterfaceUserSlice} from "../typescript/interfaceUserSlice"
import axios from "../axios"
export const UserLogin = createAsyncThunk("/users/authentication", async (user:{email:string,password:string}) => {
try{
const res = await axios.post("/users/authentication", user)
...
} catch(err){
...
}
})
const initialState:InterfaceUserSlice = {
...
}
const userSlice = createSlice({
name: "user",
initialState,
reducers: {},
extraReducers: (builder) => {}
})
export default userSlice.reducer
Login.tsx page file:
import React, {useState} from "react"
import { useDispatch } from "react-redux"
import { UserLogin } from "../redux/userSlice"
const Login = () => {
const dispatch = useDispatch()
const [email, setEmail] = useState<string>("")
const [password, setPassword] = useState<string>("")
function LoginRequest(){
dispatch(UserLogin({email,password})) //This is the point that I have the error which says: "Argument of type 'AsyncThunkAction<void, { email: string; password: string; }, AsyncThunkConfig>' is not assignable to parameter of type 'AnyAction'."
}
return (
...
)
}
export default Login
If you use TypeScript you always should set return type and arguments for your asyncThunk in genric
export const UserLogin = createAsyncThunk<return type, arguments>("/users/authentication", async (user) => {
try{
const res = await axios.post("/users/authentication", user)
...
} catch(err){
...
}
})
And also you should create custom hook useDispatch and useSelector
import { useSelector, useDispatch, TypedUseSelectorHook } from "react-redux";
import type { RootState, AppDispatch } from "../redux/store";
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
And main reducer file should have such look:
import { configureStore } from "#reduxjs/toolkit";
import userleSlice from "./slice/userSlice";
export const store = configureStore({
reducer: {
user: userSlice,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Try passing the type of the args to createAsyncThunk:
type ReturnedType = any // The type of the return of the thunk
type ThunkArg = { email:string, password:string }
export const UserLogin = createAsyncThunk<ReturnedType, ThunkArg>("/users/authentication", async (user) => {
try{
const res = await axios.post("/users/authentication", user)
...
} catch(err){
...
}
})
Hello I have a reactjs context which checks the localstorage token and then passes data down from it to the other componenets. However if a user tries to load a page without having access I want it to navigate back to the login component. However I keep getting this error "{message: 'Invalid token specified'}" Because the token doesn't exist in this scenario. How can I correctly make this redirect and work if the token doesn't exist at all?
import { createContext, useState, useEffect } from "react";
import jwt_decode from "jwt-decode";
const UserContext = createContext({});
export const UserProvider = ({ children }) => {
const [firstName, setFirstName] = useState();
const [lastName, setLastName] = useState();
const [email, setEmail] = useState();
function checkAuth() {
const token = localStorage.getItem("access_token");
if (localStorage.getItem("access_token") === '') {
navigate('/auth');
return false;
}
const authData = jwt_decode(token);
setFirstName(authData.firstname);
setLastName(authData.lastName);
setEmail(authData.email);
}
useEffect(()=>{
checkAuth
},[checkAuth])
return (
<UserContext.Provider value={{
firstName, setFirstName,
lastName, setLastName,
email,setEmail
}}>
{children}
</UserContext.Provider>
)
}
export default UserContext;
I'm using Firebase to keep track of all the users. I was able to connect Firebase. User registration is working fine.
I'm having issues with logging the users. When I enter login and password, the app doesn't redirect me to the right screen, and also throws this warning [Unhandled promise rejection: TypeError: navigation.navigation is not a function. (In 'navigation.navigation("myProfile")', 'navigation.navigation' is undefined)]
Here is how I do it
import { useNavigation } from "#react-navigation/native";
import React, { useEffect, useState } from "react";
import { View, ...} from "react-native";
import { auth } from "../firebase";
const Profile = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const navigation = useNavigation();
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
navigation.navigation("myProfile");
}
});
return unsubscribe;
}, []);
const signIn = () => {
auth
.signInWithEmailAndPassword(email, password)
.then((userCredentials) => {
const user = userCredentials.user;
console.log("Sign In user.email = " + user.email);
})
.catch((error) => alert(error.message));
};
//more code
I also tried this, but it didn't help
import React, { useEffect, useState } from "react";
import { View, ...} from "react-native";
import { auth } from "../firebase";
const Profile = ({navigation}) => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
if (user) {
navigation.navigation("myProfile");
}
});
return unsubscribe;
}, []);
//more code
I'm not sure if I need to navigation.navigation("myProfile") onPress or it has to be inside of the the useEffect
Use
navigation.navigate("myProfile")
instead of
navigation.navigation("myProfile");
Also you can destruct your hook object like this
const { navigate } = useNavigation()
in order to just write
navigate("myProfile")
I'm not sure if I need to navigation.navigation("myProfile") onPress or it has to be inside of the the useEffect
Most of the cases you should move the user to home screen (logged in user screen) when the signIn function returns true and the user is logged in, so in this case I think you have to use navigate("myProfile") inside of
auth
.signInWithEmailAndPassword(email, password)
.then((userCredentials) => {
...your code
navigation.navigate("myProfile")
})