const Bookmarks = ({ category }) => {
const [data, setData] = useState([])
const [error, setError] = useState()
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true);
fetch(`${endpoint}/${category}`)
.then(res => res.json())
.then(d => {
setData(d)
setLoading(false)
})
.catch(e => setError(e))
}, [category])
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
const Bookmarks = ({ category }) => {
const [data, setData] = useState([])
const [error, setError] = useState()
const [loading, setLoading] = useState(false)
useEffect(() => {
setLoading(true);
fetch(`${endpoint}/${category}`)
.then(res => res.json())
.then(d => {
setData(d)
setLoading(false)
})
.catch(e => setError(e))
}, [category])
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
const Bookmarks = ({ category }) => {
const [data, setData] = useState([])
const [error, setError] = useState()
const [loading, setLoading] = useState(false)
useEffect(() => {
let ignore = false
setIsLoading(true)
fetch(`${endpoint}/${category}`)
.then(res => {
if (!res.ok) {
throw new Error('Failed to fetch')
}
return res.json()
})
.then(d => {
if (!ignore) {
setData(d)
setError(undefined)
}
})
.catch(e => {
if (!ignore) {
setError(e)
setData(undefined)
}
})
.finally(() => {
if (!ignore) {
setIsLoading(false)
}
})
return () => {
ignore = true
}
}, [category])
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
- React Query docs
const Bookmarks = ({ category }) => {
const [data, setData] = useState([])
const [error, setError] = useState()
const [loading, setLoading] = useState(false)
useEffect(() => {
let ignore = false
setIsLoading(true)
fetch(`${endpoint}/${category}`)
.then(res => {
if (!res.ok) {
throw new Error('Failed to fetch')
}
return res.json()
})
.then(d => {
if (!ignore) {
setData(d)
setError(undefined)
}
})
.catch(e => {
if (!ignore) {
setError(e)
setData(undefined)
}
})
.finally(() => {
if (!ignore) {
setIsLoading(false)
}
})
return () => {
ignore = true
}
}, [category])
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
import { useQuery } from '@tanstack/react-query';
const Bookmarks = ({ category }) => {
const { isLoading, data, error } = useQuery({
queryKey: ['bookmarks', category],
queryFn: () =>
fetch(`${endpoint}/${category}`).then((res) => {
if (!res.ok) {
throw new Error('Failed to fetch')
}
return res.json()
}),
})
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
import { useIssues } from './useIssues';
const Bookmarks = ({ category }) => {
const { isLoading, data, error } = useIssues(category)
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
import { useQuery } from '@tanstack/react-query';
export const useIssues = (category) => {
return useQuery({
queryKey: ['bookmarks', category],
queryFn: ({ signal }) =>
fetch(`${endpoint}/${category}`, { signal }).then((res) => {
if (!res.ok) {
throw new Error('Failed to fetch')
}
return res.json()
}),
})
}
import { useQuery } from '@tanstack/react-query';
const Bookmarks = ({ category }) => {
const { isLoading, data, error } = useQuery({
queryKey: ['bookmarks', category],
queryFn: () =>
fetch(`${endpoint}/${category}`).then((res) => {
if (!res.ok) {
throw new Error('Failed to fetch')
}
return res.json()
}),
})
if (loading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
useQuery({
queryKey: ['issues'],
queryFn: () => axios.get('/issues').then((response) => response.data),
})
useQuery({
queryKey: ['issues'],
queryFn: () =>
Promise.resolve([
{ id: '1', title: 'A Feature', status: 'closed' },
{ id: '2', title: 'A Nug', status: 'open' },
]),
})
How can I define a baseUrl with RQ?
How can I access response headers with RQ?
How can I make GraphQL requests with RQ?
function useIssues() {
return useSelector(state => state.issues)
}
function useIssues() {
return useStore(state => state.issues)
}
const useIssues = () =>
useQuery({
queryKey: ['issues'],
queryFn: () => axios.get('/issues').then((response) => response.data)
})
const useIssues = (select) =>
useQuery({
queryKey: ['issues'],
queryFn: () => axios.get('/issues').then((response) => response.data),
select
})
const useIssueCount = () {
return useIssue((issues) => issues.length)
}
export const useIssues = () =>
useQuery({
queryKey: ['issues'],
queryFn: fetchTodos
})
function ComponentOne() {
const { data } = useIssues()
}
function ComponentTwo() {
// ✅ will get exactly the same data as ComponentOne
const { data } = useIssues()
}
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<ComponentOne />
<ComponentTwo />
</QueryClientProvider>
)
}
import { useDispatch } from 'react-redux';
const dispatch = useDispatch()
const { data } = useQuery({
queryKey: ['issues'],
queryFn: () => axios.get('/issues').then((response) => response.data),
})
React.useEffect(() => {
if (data) {
dispatch(setIssues(data))
}
}, [data])
const [issues, setIssues] = React.useState()
useQuery({
queryKey: ['issues'],
queryFn: () => axios.get('/issues').then((response) => response.data),
onSuccess: (data) => setIssues(data)
})
Caching
Deduping requests
Updating stale data
Background updates
03
02
04
01
Managing memory & GC
05
06
Performance opt.
refetchOnWindowFocus
refetchOnMount
refetchOnReconnect
QueryKey
refetchOnWindowFocus
refetchOnMount
QueryKey
refetchOnReconnect
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 2 * 60 * 1000 // 2 minutes
}
}
})
useQuery({
queryKey,
queryFn,
staleTime: 5 * 60 * 100 // 5 minutes
})
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// ✅ globally default to 20 seconds
staleTime: 1000 * 20,
},
},
})
// 🚀 everything todo-related will have
// a 1 minute staleTime
queryClient.setQueryDefaults(
todoKeys.all,
{ staleTime: 1000 * 60 }
)
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
retryOnMount: false,
retry: 0,
refetchInterval: 0,
}
}
})
refetchOnWindowFocus
refetchOnMount
QueryKey
refetchOnReconnect
const useIssues = (filters) =>
useQuery({
queryKey: ['issues', filters],
queryFn: () => axios
.get('/issues?filters=${filters}')
.then((response) => response.data),
})
const useIssues = () => {
const filters = useSelector((state) => state.filters)
return useQuery({
queryKey: ['issues', filters],
queryFn: () => axios.get('/issues?filters=${filters}').then((response) => response.data),
})
}
- src
- features
- Profile
- index.tsx
- services.ts
- queries.ts
- keys.ts
- Issues
- index.tsx
- services.ts
- queries.ts
- keys.ts
const IssueKeys = {
all: () => ['issues'] as const,
lists: () => [...issueKeys.all, 'list'] as const,
list: (filters: string) => [...issueKeys.lists(), { filters }] as const,
details: () => [...issueKeys.all, 'detail'] as const,
detail: (id: number) => [...issueKeys.details(), id] as const,
}
export const IssuesService = {
getAllIssues: (filters) => {
return axios.get('/issues?filters=${filters}').then((response) => response.data)
}
}
import IssuesService from './services'
import IssueKeys from './keys'
const useIssues = (filters) => {
// 🚀 get list of issues
return useQuery({
queryKey: IssueKeys.list(filters),
queryFn: IssuesService.getIssues(filters),
})
}
// 🕺 remove everything related
// to the issue feature
queryClient.removeQueries({
queryKey: IssueKeys.all()
})
// 🚀 invalidate all the lists
queryClient.invalidateQueries({
queryKey: IssueKeys.lists()
})
// 🙌 prefetch a single issue
queryClient.prefetchQueries({
queryKey: IssueKeys.detail(id),
queryFn: IssuesService.getIssue(id),
})
// 🙌 fetch a single issue
useQuery({
queryKey: IssueKeys.detail(id),
queryFn: IssuesService.getIssue(id),
})
const useIssues = (filters) => {
return useQuery({
queryKey: ['issues', filters],
queryFn: () => axios.get('/issues?filters=${filters}').then((response) => response.data),
})
}
const IssuesList = (filters) => {
const filters = useSelector((state) => state.filters)
const { data, refetch } = useIssues(filters)
const handleFilterChange = () => {
refetch()
}
return (
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</div>
))}
</ul>
)
}
useQuery({
queryKey: ['issues'],
queryFn: fetchIssues,
})
// 🚨 this won't work
useInfiniteQuery({
queryKey: ['issues'],
queryFn: fetchInfiniteIssues,
})
// ✅ choose something else instead
useInfiniteQuery({
queryKey: ['infiniteIssues'],
queryFn: fetchInfiniteIssues,
})
Async State Manager
Data sync lib
Keys as factories
03
02
01
n.mitrovic@vegait.rs
You can find me at
Link to the slides