Skip to content

Instantly share code, notes, and snippets.

@nurmdrafi
Created November 15, 2022 16:07
Show Gist options
  • Save nurmdrafi/f79e379d8d7cb601c1519c0c4a2c4d4e to your computer and use it in GitHub Desktop.
Save nurmdrafi/f79e379d8d7cb601c1519c0c4a2c4d4e to your computer and use it in GitHub Desktop.
https://www.youtube.com/watch?v=7exOfIAKuWU&list=LL&index=2&t=35s

βœ” // 1. using default fetch, useEffect, useState

const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true)
useEffect(() => {
	const fetchData = async () => {
		const res = await fetch(url);
		const data = await res.json()
		setData(data)
		setIsLoading(false)
	}
	fetchData()
},[])

if(isLoading){
	return <h2>Loading...</h2>
}

> development mode e useEffect 2 bar call hote pare,
avoid korar jonno <React.StrictMode> off korte hobe


βœ” // 2. axios
> akbare resolve kore object akare data dey

import axios from "axios"
const [data, setData] = useState([]);
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState("")

useEffect(() => {
	const fetchData = async () => {
		const res = await axios.get(url);
		setData(res.data)
		setIsLoading(false)
	}
	fetchData()
},[])

// best approach for error handling
// after response
> data array
> status


// using promises => .then.then.then.catch
useEffect(() => {
	axios
	.get(url).then((res) => {
		setData(res.data)
		setIsLoading(false)
	})
	.catch(error => {
		setError(error.message)
		setIsLoading(false)
	})
}, [])

if(isLoading){
	return <h2>Loading...</h2>
}

if(error){
	return <h2>{error}</h2>	
}

// using async/await + error handling

useEffect(() => {

const getData = async () => {
	try {
		const res await axios.get(url)
		setData(res.data)
	} catch{ (error) => {
		setError(error.message)
		setIsLoading(false)
	}
	
}	
}
}, [])


// best way to use axios
> setup baseUrl same folder
or
> create axios.js
> const API = axios.create({
	baseURL: "localhost"
})
export default API


// MORE: error handling
> promise can be handled two ways try/catch block with async/await OR .then().catch() method

// Then and Catch \\
axios.get('/my-api-route')
    .then(res => {
        // Work with the response...
    }).catch(err => {
        // Handle error
        console.log(err);
    });
// Try/Catch with async/await \\
try {
    let res = await axios.get('/my-api-route');

    // Work with the response...
} catch (err) {
    if (err.response) {
        // The client was given an error response (5xx, 4xx)
        console.log(err.response.data);
        console.log(err.response.status);
        console.log(err.response.headers);
    } else if (err.request) {
        // The client never received a response, and the request was never left
    } else {
        // Anything else
    }
}

// axios interceptor
Axios interceptors are function that Axios call for every request

we can intercept while request or while response



// 3. stale-while-revalidate
> stale old data
> fetch related kono help kore na,we can use fetch or axios with swr
> help state management after fetch data

// fetcher function
const fetcher = async (...arg) => {
	const res = await axios.get(...arg);
		return res.data;
}

const {data, error} = useSWR(url, fetcher, {
	suspense: true
})
> see doc for what srw {...returns}
> fetcher function take custom hook er modde store kore bar bar use korte hobe

if(error){
	return <p>error message</p>
}

// 4. react-query
> react query context api er moto data gulo { QueryClientProvider } provider er modde sent kore
> jei component gula data receive korbe segulo ke provider diye wrap korte hobe
> value hisabe client={queryClient} dite hobe, jeta akta class

import {QueryClientProvider, QueryClient} from "react-query"
const client = new QueryClient({
	defaultOptions: {
	queries: {
		suspense: true
	}
	}
})
const {isLoading,
    data: notes,
    isError,
    error,
    refetch} = useQuery("cache key", () => )

> 1st param - cache key diye jodi data fetch korte chay tahole new kore request pathabe na cache key te store kora data take send korbe

<Suspense fallback={<Loading/>}>
	<Data />
</Suspense>




🎯 React Query
βœ” what is react query
> a library for fetching data in react application

βœ” why?
1. since react is a ui library, there is no specific pattern for data fetching
2. typically we use useEffect hook for data fetching and useState hook to maintain component state like loading error or resulting data
3. if the data is needed throughout the app, we tend to use state management libraries
4. most of the state management libraries are good for working with client state
	ex: "theme" for application/ whether a modal is open
5. state management libraries are not great for working with async or server state

βœ” client state
Persisted in your app memory and accessing or updating it is synchronous

βœ” server state
> Persisted remotely and requires asynchronous APIs for fetching or updating
> has shared ownership
> data can be updated by someone else without your knowledge
> UI data may not be in sync with the remote data
> challenging when you have to deal with caching, deduping multiple requests for the same data, updating stale data in the background, performance optimizations etc.

βœ” course content
1. basic queries
2. poll data
3. React-Query dev tools
4. create reusable query hooks
5. query by id
6. parallel queries
7. dynamic queries
8. dependent queries
9. infinite & paginated queries
10. update data using mutation
11. invalidate queries
12. optimistic updates
13. axios interceptor

βœ” project setup
1. new react project using create-react-app
2. setup an api endpoint that serves mock data for use in our application
3. setup react router and a few routes in the application
4. fetch data the traditional way using useEffect and useState

βœ” create fake data
> yarn add json-server
> create db.json at root
> scripts : {
	"server-json": "json-server --watch db.json --port 4000"
}
> yarn serve-json

βœ” setup react-query

1. import QueryClientProvider and QueryClient
import {QueryClientProvider, QueryClient} from "react-query"
const client = new QueryClient({
	defaultOptions: {
		queries: {
			suspense: true
		}
	}
})

2. wrap children with QueryClientProvider and pass client={QueryClient}

3. if need suspense, but it will causes boundary error
const client = new QueryClient({
	defaultOptions: {
		queries: {
			suspense: true
		}
	}
})

also set {suspense: true} inside useQuery hook after fetcher function

4. need atlease 2 param
useQuery("unique-key", functionWhichReturnPromise)
functionWhichReturnPromise: this function will make request to server



βœ” basic implementation
import { useQuery } from "react-query";
import useAuthUserContext from "../context/AuthUserContext";

const { authUser } = useAuthUserContext();

// usually getting context takes time
// if found then try to fetch
const { isLoading, data: notes, isError, error, refetch} = useQuery("notes", () => authUser.user.email && getByEmail(authUser.user.email));

// if undefined at 1st approach then refetch again
  if (!notes) {
    refetch();
  }

// if isLoading(fetching data) show loading component
  if (isLoading) {
    return <Loading />;
  }

// if error occued while fetching show error message  
  if (isError) {
    toast.error(error.message, {
      id: "signUp error",
    });
  }

// finally data found then pass to child component
{notes && <NoteList notes={notes} refetch={refetch} />}

// then our child component
if {notes.length === 0 ? (<p>No Data<p>) : (notes.map((note) => <Note note={note}></Note>))}


βœ” react query dev tool
> go to wrapper and import
import {ReactQueryDevtools} from "react-query/devtools"

> use this component before ending wrapper
<QueryClientProvider client={queryClient}>
  <App />
  <ReactQueryDevtools initialIsOpen={false} position="bottom-right"/>
</QueryClientProvider>


βœ” query cache
***cacheTime determines for how long a certain response is supposed to stay in cache before it gets garbage collected.

> by default react query will always cache request responst for 5 minitues(300000 millisecond)
> react query saved caches data by "unique-key" variable
> if cache which saved by unique-key is exist then show data without loading
> if data changes isLoading: false, isFetching: true
> after fetch update data isFetching: false
> if we want to change default cache time

useQuery("unique-key", fetcherFunction, { cacheTime: 5000})

> after finised cacheTime query cache data is garbage collected

βœ” stale time (old data)

*** staleTime determines for how long a certain response will still be considered fresh (or not stale), dismissing the need for a new request.
> for doesnt necessarily changed ofted
> like our productslist or userlist are not changed ofted
> then we can use our cached query results and reduce unnecessary request
> if data changes we can set cache time for 30 sec

useQuery("unique-key", fetcherFunction, { staleTime: 30000})

> default staleTime:0

βœ” cacheTime vs staleTime
https://stackoverflow.com/a/72833550/15497939 βœ”

https://tkdodo.eu/blog/practical-react-query#the-defaults-explained βœ”

https://medium.com/doctolib/react-query-cachetime-vs-staletime-ec74defc483e πŸ€”

StaleTime: The duration until a query transitions from fresh to stale. As long as the query is fresh, data will always be read from the cache only - no network request will happen! If the query is stale (which per default is: instantly), you will still get data from the cache, but a background refetch can happen under certain conditions.



CacheTime: The duration until inactive queries will be removed from the cache. This defaults to 5 minutes. Queries transition to the inactive state as soon as there are no observers registered, so when all components which use that query have unmounted.


βœ” Refetch(pending...)
{
	
refetchOnMOunt: true

}
> If set to true, the query will refetch on mount if the data is stale.

βœ” Mutations βœ”βœ”βœ”
Mutations are used for Create, Update or Delete data from Database

// create
createNewNote(newNote);

export const useCreateNewNote = () =>{
	return useMutation()
}

// use
const {mutate: createNote} = useCreateNewNote()

const handleCreateNote = () =>{
	// newNote object
	createNote(newNote)
}



βœ” Axios Interceptor
import axios from "axios"

const client = axios.create({baseUrl: "localhost"})

export const request = ({...options}) => {
	client.default.headers.commoon.Authorization = `Bearer token`
	const onSuccess = res => res
	const onError = error =>{
		// optionally catch errors and add additional logging here
		// redirect login page
		return error
	}
	return client(options).then(onSuccess).catch(onError)
}

import {request} from "../utils/axios-utils"

export const fetchData = () {
	// return axios.get("localhost/users")
	return request(url: "/users")
}

export const addUser = (user) {
	// return axios.post("localhost/post", user, {{headers:}})
	return request({url: "/post", method: "post", data: user})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment