I have two custom hooks i.e useFetch and useAuth. useAuth has all API calls methods (e.g logIn, logOut, register, getProfile etc) and they use useFetch hook method for doing API calls. useFetch also uses these methods for example logOut method when API return 401, setToken etc. So, they both need to share common methods. But that results into circular dependency and call size stack exceeded error. How to manage this
UseFetch.js
import React, { useState, useContext } from "react";
import { AuthContext } from "../context/authContext";
import { baseURL } from "../utils/constants";
import { useAuth } from "./useAuth";
const RCTNetworking = require("react-native/Libraries/Network/RCTNetworking");
export const useFetch = () => {
const {token, setAuthToken, isLoading, setIsLoading, logIn, logOut} = useAuth();
const fetchAPI = (method, url, body, isPublic, noBaseURL) => {
setIsLoading(true);
const options = {
method: method
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
return fetch(url, options, isRetrying).then(() => {
......
})
......
};
return { fetchAPI };
};
UseAuth.js
import React, { useContext, useEffect } from "react";
import { AuthContext } from "../context/authContext";
import { useFetch } from "./useFetch";
export const useAuth = () => {
const {
removeAuthToken,
removeUser,
setUser,
...others
} = useContext(AuthContext);
const { fetchAPI } = useFetch();
const register = (body) => {
return fetchAPI("POST", "/customers/register", body, true);
};
const logIn = (body) => {
return fetchAPI("POST", "/customers/login", body, true);
};
const logOut = () => {
return (
fetchAPI("POST", "/customers/logout")
.catch((err) => console.log("err", err.message))
.finally(() => {
removeAuthToken();
removeUser();
})
);
......
};
return {
...others,
register,
logIn,
logOut,
};
};
Related
I have a login component from which i try to save the authenticated user's data into the AuthContext using a custom useAuth() hooks. But when i set the value from within login component AuthContext never get updated and the auth object is null, but when i set the AuthContext from within another componenet it got updated as expected and i could access it from other component. what might be the reason behind this weird behaviour
import useAuth from "../../hooks/useAuth";
const {auth, setAuth} = useAuth();
useEffect(() => {
console.log(auth)
}, [auth]);
const submitHandler = async (e) => {
e.preventDefault();
try {
const response = await axios.post(LOGIN_URL,
JSON.stringify({ username, password }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true
}
);
const accessToken = response.data ? response.data.accessToken : null;
const user = response.data ? response.data.user : null;
// setAuth({
// accessToken:accessToken,
// user:user
// });
const obj = {
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIser5taSI6Im1AMkh0a3k2KnQ5aWZlIn0.eyJpc3MiOiJodHRwczpcL1wvYXBpLmNoZWNrc3RhbGwuY29tIiwiYXVkIjoiaHR0cHM6cdfrt2FwaS5jaGVja3N0YWxsLmNvbSIsImp0aSI6Im1AMkh0a3k2KnQ5aWZlIiwiaWF0IjoxNjY3NTkwMjg3LCJleHAiOjE2Njc1OTA1ODcsInVpZCI6MX0.4LYi3eDW6mfKB9H_vOjhfqttKoT1dGUdJuisU3esdwt",
"user": {
"username": "bob01",
"email": "bob#yahoo.com",
"created_at": 1659239223,
"full_name": "Bob L",
"role": 40,
"phone_number": "199765432",
"last_login": 16647884254
}
}
setAuth(obj);
setUsername('');
setPassword('');
//navigate(-1);
setModalShow(false);
}catch(err){
if (err.response) {
//do something
} else{
//do something
}
}
}
after async call the useEffect got called and log the result as expected but when i set the same result into the AuthContext through the useAuth hooks it never go updated
Below is my useAuth hooks
import { useContext, useDebugValue } from "react";
import AuthContext from "../context/AuthProvider";
const useAuth = () => {
const { auth } = useContext(AuthContext);
if (auth === undefined) {
throw new Error("useUserContext can only be used inside AuthProvider");
}
return useContext(AuthContext);
}
export default useAuth;
and also my AuthContext Provider
import React ,{ createContext, useState, useMemo } from "react";
const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [auth, setAuth] = useState({});
const [persist, setPersist] = useState(JSON.parse(localStorage.getItem("persist")) || false);
const value = useMemo(() => ({
auth, setAuth ,persist ,setPersist
}), [auth, setAuth, persist,setPersist]);
return (
<AuthContext.Provider value={value}>{children}</AuthContext.Provider>
)
}
export default AuthContext;
I am making a app using ionic-react. Calling a api in useEffect but api is not getting call.
import classes from './index.module.scss';
import React, { useEffect, useState } from 'react';
import { IonContent, IonPage } from '#ionic/react';
import { LoggedInHeader, WalletCard } from '../../components';
import { Stack } from '#mui/material';
import { wallet } from '../../apis';
interface WalletProps { };
export const Wallet = (props: WalletProps) => {
const [walletHistory, setWalletHistory] = useState<any[]>([]);
useEffect(() => {
console.log("is this comingggggggggggggg")
let data = {
"user_id": localStorage.getItem("userid")
}
wallet(data)
.then((response)=> {
setWalletHistory(response.data.data)
})
.catch((error)=>{
console.error("Getting error at auth: ", error);
})
}, [])
return (
<IonPage>
<IonContent fullscreen>
<LoggedInHeader />
some content
</IonContent>
</IonPage>
)
}
If i add a dependency in useeffect then api is calling infinite time.
Wallet api calling file
instance.interceptors.request.use(function (config: AxiosRequestConfig) {
const token = localStorage.getItem('usertoken') || '';
if (token) {
const headers: AxiosRequestHeaders = {
Authorization: token
};
config.headers = headers;
}
return config;
});
export const wallet = (user_id:any) => instance({
method: 'GET',
url: `/customer/wallet_history`,
data: user_id,
responseType: 'json'
});
This is my api calling file and i am using axios instance
I am following a redux example to create a slice in react-redux, in my console i have no errors and the state has my vessels but it's just empty and doesnt have any data from my axios api call,
my backend is running and the api call is working fine.
vesselSlice :
import { createSlice } from "#reduxjs/toolkit";
import { api } from "../components/pages/screens/HomeScreen";
const vesselSlice = createSlice({
name: "vessels",
initialState: {
vessels: [],
},
reducers: {
getVessels: (state, action) => {
state.vessels = action.payload;
},
},
});
export const vesselReducer = vesselSlice.reducer;
const { getVessels } = vesselSlice.actions;
export const fetchVessels = () => async (dispatch) => {
try {
await api
.get("/vessels")
.then((response) => dispatch(getVessels(response.data)));
} catch (e) {
return console.error(e.message);
}
};
HomeScreen :
import React, { useEffect } from "react";
import VesselCard from "../../VesselCard";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { vesselSlice } from "../../../features/vesselSlice";
export const api = axios.create({
baseURL: "http://127.0.0.1:8000/api/vessels/info",
headers: {
"Content-Type": "application/json",
},
});
function HomeScreen() {
const { vessels, isLoading } = useSelector((state) => state.vessels);
return (
<div>
Fleet vessels :
<div className="fleet-vessels-info">
{vessels.map((vessel) => (
<VesselCard vessel={vessel} />
))}
</div>
</div>
);
}
export default HomeScreen;
You have to actually call the function fetchVessels. In this simple example, I would do it using useEffect:
import React, { useEffect } from "react";
import VesselCard from "../../VesselCard";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { vesselSlice, fetchVessels } from "../../../features/vesselSlice";
export const api = axios.create({
baseURL: "http://127.0.0.1:8000/api/vessels/info",
headers: {
"Content-Type": "application/json",
},
});
function HomeScreen() {
const { vessels, isLoading } = useSelector((state) => state.vessels);
const dispatch = useDispatch();
// This part:
useEffect(() => {
fetchVessels(dispatch);
}, [dispatch]);
return (
<div>
Fleet vessels :
<div className="fleet-vessels-info">
{vessels.map((vessel) => (
<VesselCard vessel={vessel} />
))}
</div>
</div>
);
}
export default HomeScreen;
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
I want to integrate to React query for fetching the data from server.
So far I've been fetching the rest api via Axios.
I have created a custom hook for fetching and want to transform and implement with react query.
While trying to implement the same logic I encountered an error trying to destructure the fetched data const { data } = useApiRequest(headersUrl):
error - TypeError: (0 , _hooks_useApiRequest__WEBPACK_IMPORTED_MODULE_1__.UseApiRequest) is not a function
Here is the old logic for fetching
import * as React from "react";
import { useState, useEffect } from "react";
import axios from "axios";
import { HeaderToken } from "../services/api";
export const useApiRequest = (url: any) => {
const [data, setData] = useState<[] | any>([]);
useEffect(() => {
const fetchData = () => {
axios
.get(url, {
headers: {
Authorization: `Basic ${HeaderToken}`,
},
})
.then((response) => {
setData(response.data);
})
.catch((error) => console.error(error));
};
fetchData();
}, [url]);
return { data };
};
And here is how I'm trying to convert it:
import { HeaderToken } from "../services/api";
import { useQuery } from "react-query";
export const useApiRequest = (url: any) => {
const { isLoading, data } = useQuery("bc", async () => {
const response = await fetch(url, {
method: "get",
headers: {
Authorization: `Basic ${HeaderToken}`,
"Content-Type": "application/json",
},
});
if (!response.ok) throw new Error(response.statusText);
return await response.json();
});
return { data };
};
I can't see the problem, actually, I tried the same code you shared with a local API I have and it's working
The Hook
import { useQuery } from 'react-query'
export const clientAPI = (url) => {
const { isLoading, data } = useQuery("bc", async () => {
const response = await fetch(url, {
method: "get"
});
if (!response.ok) throw new Error(response.statusText);
return await response.json();
});
return { data };
};
React Component
import * as React from "react";
import { clientAPI } from "../hooks/clientAPI";
export default function Home() {
const { data } = clientAPI('http://localhost:5000/')
return (
<div>
{JSON.stringify(data)}
</div>
)
}
_app.js
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
const queryClient = new QueryClient()
function MyApp({ Component, pageProps }) {
return (<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
</QueryClientProvider>)
}
export default MyApp
I'm using next#11.1.2, react-query#3.28.0
what are the versions you are using?