Mutation is not a function? - reactjs

I want to use social login by using graphql.
I want to get accessToken from social login by using kakaologin function and then i use it to make token by using kakakoLoginMutation function.
I try to make const name is access_token(=="123")
and if i success to login by kakao, i get accss_token by authObj. and then i set it to const access_token by using setAccess_token(useState).
console.log(authObj.access_token) show correct strings.
but after setAccess_token(authObj.access_token), console.log(access_token) show "123".
I think that console.log(access_token) have to show not "123" ohter strings.
and the other error is KakaoLoginMutation is not a function.
import { useState } from "react";
import { useMutation } from "react-apollo-hooks";
import { KAKAO_LOGIN, LOCAL_LOG_IN } from "./AuthQuery";
export default () => {
const [access_token, setAccess_token] = useState("123");
const localLogInMutation = useMutation(LOCAL_LOG_IN);
const kakaoLoginMutation = useMutation(KAKAO_LOGIN, {
variables: { provider: "kakao", accessToken: access_token },
});
const kakaoLogin = async (e) => {
e.preventDefault();
window.Kakao.Auth.login({
success: function (authObj) {
setAccess_token(authObj.access_token);
console.log(authObj.access_token);
console.log(access_token);
},
});
const {
data: { socialAuth: token },
} = await kakaoLoginMutation();
console.log(token);
if (token !== "" && token !== undefined) {
localLogInMutation({ variables: { token } });
}
};
return (
<a href="#" onClick={kakaoLogin}>
<h1>카카오로그인</h1>
</a>
);
};
Query.js
import { gql } from "apollo-boost";
export const KAKAO_LOGIN = gql`
mutation SocialAuth($provider: String!, $accessToken: String!) {
socialAuth(provider: $provider, accessToken: $accessToken) {
token
}
}
`;

Related

React Native - Authentication - trigger value to change Auth & UnAuth Stack Navigators

Here my App.js
import React, { useEffect } from "react";
import { NavigationContainer } from "#react-navigation/native";
import AuthStore from "./src/stores/AuthStore";
import AuthStackNavigator from "./src/navigation/AuthStackNavigator";
import UnAuthStackNavigator from "./src/navigation/UnAuthStackNavigator";
const App = () => {
useEffect(() => {
console.log("APP JS", AuthStore.userAuthenticated);
}, [AuthStore.userAuthenticated]);
return <NavigationContainer>
{AuthStore.userAuthenticated ? <AuthStackNavigator /> : <UnAuthStackNavigator />}
</NavigationContainer>;
};
export default App;
The AuthStore value of userAuthenticated is computed and updated on auto login or login.
Here AuthStore.js
import { userLogin, userRegister } from "../api/AuthAgent";
import { clearStorage, retrieveUserSession, storeUserSession } from "../utils/EncryptedStorage";
import { Alert } from "react-native";
import { computed, makeObservable, observable } from "mobx";
import { setBearerToken } from "../config/HttpClient";
class AuthStore {
user = {};
token = undefined;
refreshToken = undefined;
decodedToken = undefined;
constructor() {
makeObservable(this, {
token: observable,
refreshToken: observable,
user: observable,
decodedToken: observable,
userAuthenticated: computed,
});
this.autoLogin();
}
async doLogin(body) {
const resp = await userLogin(body);
console.log("AuthStore > userLogin > resp => ", resp);
if (resp.success) {
this.decodedToken = await this.getDecodedToken(resp.token);
this.setUserData(resp);
storeUserSession(resp);
} else {
Alert.alert(
"Wrong credentials!",
"Please, make sure that your email & password are correct",
);
}
}
async autoLogin() {
const user = await retrieveUserSession();
if (user) {
this.setUserData(user);
}
}
setUserData(data) {
this.user = data;
this.token = data.token;
setBearerToken(data.token);
}
get userAuthenticated() {
console.log('AuthStore > MOBX - COMPUTED userAuthenticated', this.user);
if (this.token) {
return true;
} else return false;
}
async logout() {
await clearStorage();
this.user = undefined;
this.token = undefined;
this.refreshToken = undefined;
this.decodedToken = undefined;
}
}
export default new AuthStore();
The main problem is that the AuthStore.userAuthenticated value even when it changes on AuthStore it does not triggered by useEffect of the App.js.
So, when I log in or log out I have to reload the App to trigger the useEffect hook and then the navigators are only updated.
You can use useMemo hook to achive this.
const App = () => {
const [userToken, setUserToken] = useState("")
const authContext: any = useMemo(() => {
return {
signIn: (data: any) => {
AsyncStorage.setValue("token", data.accessToken);
setUserToken(data.accessToken);
},
signOut: () => {
setUserToken("");
AsyncStorage.setValue("token", "");
},
};
}, []);
return (
<AuthContext.Provider value={authContext}>
{userToken.length ? (
<UnAuthStackNavigator />
) : (
<AuthStackNavigator />
)}
)
</AuthContext.Provider>
)
}
AuthContext.ts
import React from "react";
export const AuthContext: any = React.createContext({
signIn: (res: any) => {},
signOut: () => {},
});
Now you can use this functions in any files like this:
export const SignIn = () => {
const { signIn } = useContext(AuthContext);
return (
<Button onPress={() => {signIn()}} />
)
}
If your primary purpose is to navigate in and out of stack if authentication is available or not then asynstorage is the best option you have to first
store token.
const storeToken = async (value) => {
try {
await AsynStorage.setItem("userAuthenticated", JSON.stringify(value));
} catch (error) {
console.log(error);
}
};
("userAuthenticated" is the key where we get the value )
Now go to the screen where you want this token to run turnery condition
const [token, setToken] = useState();
const getToken = async () => {
try {
const userData = JSON.parse(await AsynStorage.getItem("userAuthenticated"))
setToken(userData)
} catch (error) {
console.log(error);
}
};
Now use the token in the state then run the condition:
{token? <AuthStackNavigator /> : <UnAuthStackNavigator />}
or
{token != null? <AuthStackNavigator /> : <UnAuthStackNavigator />}

ReactJS use a token stored in a function in a class that extends React.Component

I am using tokens around my ReactJS application. This is how I can get the value of the token in a page:
pages/Profile.js
import React from 'react';
import useToken from '../useToken';
export default function Profile() {
// Get token
const { token, setToken } = useToken();
const { userObject } = useToken();
return(
<div>
<p>
<b>Name:</b> {userObject?.name}<br />
<b>Email:</b> {userObject?.email}<br />
<b>Token:</b> {token}
</p>
</div>
);
}
Now I want to use the token inside a component. However the same appoch did not work, as it gives me an error "Unexpected token. A constructor, method, accessor, or property was expected.ts(1068)".
components/MyKnownDevices.js
import React from 'react';
import axios from 'axios';
import useToken from '../useToken';
export default class MyKnownDevices extends React.Component {
// Get token
const { token, setToken } = useToken(); // <---- THIS GIVES ERROR
const { userObject } = useToken(); // <---- THIS GIVES ERROR
// Respone handler
state = {
myKnownDevices: []
}
// Call API
componentDidMount() {
let config = {
headers: {
Accept: 'application/json',
'Access-Control-Allow-Origin': '*',
rejectUnauthorized: false,
}
}
let data = {
'HTTP_CONTENT_LANGUAGE': 'no',
rejectUnauthorized: false,
}
axios.get('https://127.0.0.1:5000/api/users/get_my_known_devices', data, config)
.then(res => {
const myKnownDevices = res.data;
this.setState({ myKnownDevices });
})
};
render() {
return (
<ul>
{
this.state.myKnownDevices
.map((device, index) => {
return (
<li key={index}>
<span>{device.known_device_updated_timestamp_saying}</span>
</li>
);
}
)
}
</ul>
)
};
};
useToken.js
import { useState, useEffect } from 'react';
import jwt_decode from "jwt-decode"
// const userObject = null;
export default function useToken() {
// Get token
const getToken = () => {
const userToken = localStorage.getItem('token');
return userToken
};
// Consts
const [token, setToken] = useState(getToken());
const [userObject, setUserObject] = useState(null);
// Effect
useEffect(() => {
if(token && token !== "undefined" && !userObject){
setUserObject(jwt_decode(token));
}
},[token]);
// Save token
const saveToken = userToken => {
if(userToken === null){
localStorage.removeItem('token');
setToken(null);
}
else{
localStorage.setItem('token', userToken.token);
setToken(userToken.token);
}
};
// Return value
return {
setToken: saveToken,
token,
userObject,
}
}
How can I get the value of the token inside my component MyKnownDevices.js?
This issue here is that useToken is a hook (https://reactjs.org/docs/hooks-intro.html) and hooks can only be used in functional components.
Given your code, I think the simplest way would be to rewrite your class component as a functional component, this should do the trick :
import React, {useEffect, useState} from "react";
import axios from "axios";
import useToken from "../useToken";
export default function MyKnownDevices() {
// Get token
const { token, setToken, userObject } = useToken();
const [myKnownDevices, setMyKnownDevices] = useState([]);
// Call API
// This is the same thing as your componentDitMount
useEffect(() => {
let config = {
headers: {
Accept: "application/json",
"Access-Control-Allow-Origin": "*",
rejectUnauthorized: false,
},
};
let data = {
HTTP_CONTENT_LANGUAGE: "no",
rejectUnauthorized: false,
};
axios
.get(
"https://127.0.0.1:5000/api/users/get_my_known_devices",
data,
config
)
.then((res) => {
const myKnownDevices = res.data;
setMyKnownDevices(myKnownDevices);
});
}, []);
return (
<ul>
{myKnownDevices.map((device, index) => {
return (
<li key={index}>
<span>{device.known_device_updated_timestamp_saying}</span>
</li>
);
})}
</ul>
);
}
You must not use react hooks inside class components. You should rewrite your class component to functional

How to properly store a JWT in a cookie in react using NextJS with typescript and the useContext hook

The user is set and persists on all the pages but when the browser window is refreshed the user is logged out. Not Sure if the problem is how I'm setting the cookies, any advice or an alternative is very welcomed.
Here the UserContext file:
import { destroyCookie } from "nookies";
import { setCookie } from "nookies";
import React from "react";
// import { useCookies } from "react-cookie"
import cookie from "cookie";
import { Person, tokenAuth } from "../auth";
import { LoginFormValues } from "../components/Auth/LoginForm";
import { noop } from "../helpers";
import { COOKIE_AUTH_NAME, MONTH } from "../helpers/user";
interface UserContextProps {
user?: User;
logout: () => void;
login: (values: LoginFormValues) => Promise<void>;
}
const UserContext = React.createContext<UserContextProps>({
user: undefined,
logout: noop,
login: async () => noop(),
});
export const UseUserContext = () => React.useContext(UserContext);
interface UserContextProviderProps {
children: React.ReactNode;
user?: User;
}
export const UserContextProvider = ({
children,
user: initialUser,
}: UserContextProviderProps) => {
const [user, setUser] = React.useState<User | undefined>(initialUser);
const login = async (values: LoginFormValues) => {
try {
const tokenResponse = await tokenAuth(values);
if (tokenResponse.status === 200) {
const { token } = tokenResponse.data;
setCookie(null, COOKIE_AUTH_NAME, token, {
maxAge: 3600,
path: "/",
sameSite: "strict",
});
const userResponse = await Person();
if (userResponse.status === 200) {
setUser(userResponse.data);
}
}
} catch (error) {
let errorMessage = "failed";
if (error instanceof Error) {
errorMessage = error.message;
}
console.error(errorMessage);
}
};
const logout = () => {
setUser(undefined);
destroyCookie(null, COOKIE_AUTH_NAME);
};
return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
);
};
export default UserContext;
Im wondering if my problem is in my _app.tsx Im also using a Django rest api to retrieve the data this how I'm calling the api. This is what the file looks like:
import axios, { AxiosResponse } from "axios";
import { parseCookies } from "nookies";
export function getJwtToken() {
return sessionStorage.getItem("jwt");
}
async function request<T>(
method: "GET" | "POST" | "PATCH" | "DELETE" | "PUT" | "OPTIONS",
url: string,
data?: Record<string, unknown>
): Promise<AxiosResponse<T>> {
const cookies = parseCookies();
const { authToken } = cookies;
const baseUrl = process.env.NEXT_PUBLIC_API_URL;
const response: AxiosResponse = await axios({
method,
url: `${baseUrl}${url}`,
data,
withCredentials: true,
headers: {
"Content-Type": "application/json",
},
});
axios.defaults.headers.common["Authorization"] = authToken;
return response;
}
interface TokenData {
token: string;
}
export function tokenAuth({ email = "", password = "" }) {
return request<TokenData>("POST", "/api/client/login", {
email,
password,
});
}
export function Person() {
return request<User>("GET", `/api/client/user`);
}
This file is pretty basic and I don't think its the route of the problem.

ReactQuery in NextJs getStaticPaths

I would like to query an individual ID with useQuery hook, getStaticPaths, and getStaticProps in Next.js.
I achieved the whole list, however, I don't know how to get details for individual ID?
The code for the whole list of users is below:
import { QueryClient, useQuery } from 'react-query';
import axios from 'axios'
export default function Index() {
const { isLoading, data, isError, error } = useQuery('users', fetchUsers)
if(isLoading){
return <h2>Loading....</h2>
}
if(isError){
return<h2>{error.message}</h2>
}
return (
<>
{
data?.data.map(user => {
return <div key={user.id}>
<Link href={`users/${user.id}`}>.{user.name}. </Link>
</div>
})
}
</>
)
}
export async function getStaticProps(dehydratedState) {
const queryClient = new QueryClient()
await queryClient.prefetchQuery('users', fetchUsers)
return {
props: { dehydratedState: dehydrate(queryClient).toString()}
};
}
For individual ID's nothing worked, I left below:
It fetches a single user, but not with React Query.
import { dehydrate } from 'react-query/hydration'
import Link from 'next/link'
const fetchUsers = () => {
return axios.get('https://jsonplaceholder.typicode.com/users')
}
function User({ user }){
return (
<div>{user.name}</div>
)
}
export async function getStaticPaths() {
const res = await fetch('https://jsonplaceholder.typicode.com/users')
const users = await res.json()
const paths = users.map((user) => ({
params: { id: user.id.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${params.id}`)
const user = await res.json()
return { props: { user } }
}
export default User
The third part (this is the single ID fetch with hydrate, not simple Next.js)
import { QueryClient } from "react-query"
import { dehydrate, useQuery } from "react-query"
import axios from "axios"
const userIdFetch = (props) => {
return axios.get(`https://jsonplaceholder.typicode.com/users/${props.id}`)
}
function User(user){
const { isLoading, data, isError, error } = useQuery(['users', user.id], userIdFetch)
console.log(user)
return (
<div></div>
)
}
export async function getStaticPaths() {
const res = await fetch('https://jsonplaceholder.typicode.com/users')
const users = await res.json()
const paths = users.map((user) => ({
params: { id: user.id.toString() },
}))
return { paths, fallback: false }
}
export async function getStaticProps({ params }) {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${params.id}`)
const userFetch = await res.json()
const queryClient = new QueryClient()
await queryClient.prefetchQuery('users', userFetch)
return {
props: { dehydratedState: dehydrate(queryClient).toString()}
}
}
export default User

Handling updates in ReactJS

I'm making an application in which the user has the ability to decide if his creations are active or inactive, and the API route responsible for that is
(I'm using NextJs API routes)
import { NextApiRequest, NextApiResponse } from "next";
import { decryptCookie } from "../../../lib/cookie";
import { prisma } from "../../../lib/prisma";
interface User {
email: string;
issuer: string;
}
export default async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method !== "PUT") return res.status(405).end;
let userFromCookie: User;
try {
userFromCookie = await decryptCookie(req.cookies.auth);
if (!userFromCookie.email) {
throw new Error("Cannot find user. Unable to proceed with creation.");
}
const userEmail = userFromCookie.email;
const active = JSON.parse(req.body);
const userInDb = await prisma.user.findOne({
where: {
email: userEmail,
},
});
const response = await prisma.brainstorm.update({
data: {
active,
},
where: {
id: userInDb.id,
},
});
res.status(201).json({ response });
} catch (error) {
return res.status(500).end(error.message);
}
};
the components that contain this action receives it's data as props from a map method in a parent component
I'll put in here the whole component, but you guys can worry about the Switch that indicates the activeness and the function responsible for the change.
import React, { useState, useEffect } from "react";
import Switch from "react-switch";
import {
Container,
BrainstormInfo,
BrainstormTitle,
Active,
Group,
StormPieces,
} from "./styles";
import { Brainstorm } from "../../pages/user-dashboard";
import useFormatDate from "../../hooks/useFormatDate";
import produce from "immer";
interface Props {
brainstormData: Brainstorm;
}
const UserBrainstormCard: React.FC<Props> = ({ brainstormData }) => {
if (!brainstormData) return <h1>Loading...</h1>;
const [active, setActive] = useState(brainstormData.active);
const formatedDate = useFormatDate(
(brainstormData.createdAt as unknown) as string
);
async function handleActiveness() {
setActive(!active);
const response = await fetch("/api/brainstorm/update", {
method: "PUT",
body: JSON.stringify(active),
});
const data = await response.json();
setActive(data.response.active);
}
return (
<Container>
<BrainstormInfo>
<p>Brainstorm</p>
<p>{formatedDate}</p>
</BrainstormInfo>
<BrainstormTitle>
<h3>{brainstormData.title}</h3>
</BrainstormTitle>
<Active>
<Group>
<p>Active:</p>
<Switch
offHandleColor="#eee"
onHandleColor="#eee"
draggable={false}
onChange={handleActiveness}
checked={active}
checkedIcon={false}
uncheckedIcon={false}
height={15}
width={30}
handleDiameter={20}
offColor="#f13030"
onColor="#2dea8f"
/>
</Group>
<StormPieces>
<p>
{brainstormData.stormPieces.length}
{` `}Stormpieces
</p>
</StormPieces>
</Active>
</Container>
);
};
export default UserBrainstormCard;
The call to the API happens, but when I update the page it all goes back to what the value it was initially.
I'm pretty sure that the problem has to do with state, and that I should find a way to insert this values in the state. But I don't know a clear path on how to do it

Resources