improve: use react-query for caching
This commit is contained in:
56
src/utils/markdown-queries.js
Normal file
56
src/utils/markdown-queries.js
Normal file
@@ -0,0 +1,56 @@
|
||||
import {useQuery, useMutation, useQueryClient} from 'react-query';
|
||||
import {fetch_} from "./request-utils";
|
||||
import config from "../config";
|
||||
|
||||
export const useMarkdown = (id) => {
|
||||
return useQuery(
|
||||
["markdown", id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/${id}`),
|
||||
{
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const useIndexMarkdown = (path_id) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
["index_markdown", path_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/get_index/${path_id}`),{
|
||||
enabled: !!path_id,
|
||||
onSuccess: (data) => {
|
||||
if(data && data.id){
|
||||
queryClient.setQueryData(["markdown", data.id], data);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useMarkdownsByPath = (pathId) => {
|
||||
return useQuery(
|
||||
["markdownsByPath", pathId],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/markdown/by_path/${pathId}`),
|
||||
{
|
||||
enabled: !!pathId
|
||||
});
|
||||
};
|
||||
|
||||
export const useSaveMarkdown = () => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation(({id, data}) => {
|
||||
const url = id
|
||||
? `${config.BACKEND_HOST}/api/markdown/${id}`
|
||||
: `${config.BACKEND_HOST}/api/markdown/`;
|
||||
const method = id ? "PUT" : "POST";
|
||||
return fetch_(url, {
|
||||
method,
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
},{
|
||||
onSuccess: (res, variables) => {
|
||||
queryClient.invalidateQueries(["markdownsByPath", variables.data.parent_id]);
|
||||
queryClient.invalidateQueries(["markdown", variables.data.id]);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
90
src/utils/path-queries.js
Normal file
90
src/utils/path-queries.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useQuery, useMutation, useQueryClient } from "react-query";
|
||||
import { fetch_ } from "./request-utils";
|
||||
import config from "../config";
|
||||
|
||||
|
||||
export const usePaths = (parent_id) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useQuery(
|
||||
["paths", parent_id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/path/parent/${parent_id}`),
|
||||
{
|
||||
enabled: !!parent_id,
|
||||
onSuccess: (data) => {
|
||||
if(data) {
|
||||
for (const pth of data)
|
||||
{
|
||||
queryClient.setQueryData(["path", pth.id], pth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export const usePath = (id) => {
|
||||
const queryClient = useQueryClient();
|
||||
const cachedData = queryClient.getQueryData(["path", id]);
|
||||
|
||||
|
||||
return useQuery(
|
||||
["path", id],
|
||||
() => fetch_(`${config.BACKEND_HOST}/api/path/${id}`),
|
||||
{
|
||||
enabled: !!id,
|
||||
onSuccess: (data) => {
|
||||
console.log(`path ${id} - ${cachedData}` );
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useCreatePath = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(data) => fetch_(`${config.BACKEND_HOST}/api/path/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
{
|
||||
onSuccess: (res, variables) => {
|
||||
console.log(JSON.stringify(variables));
|
||||
queryClient.invalidateQueries(["paths", variables.parent_id]);
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useUpdatePath = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
({ id, data }) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
{
|
||||
onSuccess: (res, variables) => {
|
||||
queryClient.invalidateQueries(["paths", res.parent_id]);
|
||||
queryClient.invalidateQueries(["path", variables.data.id]);
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export const useDeletePath = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation(
|
||||
(id) => fetch_(`${config.BACKEND_HOST}/api/path/${id}`, {
|
||||
method: "DELETE",
|
||||
}),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries("paths");
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
28
src/utils/request-utils.js
Normal file
28
src/utils/request-utils.js
Normal file
@@ -0,0 +1,28 @@
|
||||
export async function fetch_(url, init = {}) {
|
||||
const token = localStorage.getItem("accessToken");
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
...(init.headers || {}),
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
};
|
||||
|
||||
const response = await fetch(url, { ...init, headers });
|
||||
|
||||
if (response.status === 304) {
|
||||
return Promise.reject(new Error("Not Modified"));
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
const error = new Error(response.statusText);
|
||||
error.data = errorData;
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
import {data} from "react-router-dom";
|
||||
|
||||
const ongoingRequests = new Map();
|
||||
|
||||
|
||||
function _default_hash_function(url, method, body){
|
||||
const url_obj = new URL(url, window.location.origin);
|
||||
|
||||
const query_params = [...url_obj.searchParams.entries()]
|
||||
.sort(([a], [b]) => a.localeCompare(b))
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join("&");
|
||||
const normalized_url = `${url_obj.origin}${url_obj.pathname}${query_params ? "?" + query_params : ""}`;
|
||||
const normalized_body = body
|
||||
? JSON.stringify(
|
||||
Object.keys(body)
|
||||
.sort()
|
||||
.reduce((acc, key) => {
|
||||
acc[key] = body[key];
|
||||
return acc;
|
||||
}, {})
|
||||
)
|
||||
: "";
|
||||
return `${method.toUpperCase()}:${normalized_url}:${normalized_body}`;
|
||||
}
|
||||
|
||||
|
||||
export async function fetch_(url, init = {}, init_options = {}){
|
||||
const default_options = {
|
||||
use_cache: true,
|
||||
cache_key: _default_hash_function(url, init.method || "GET", init.body || null),
|
||||
cache_expires: 60,
|
||||
use_token: true,
|
||||
};
|
||||
const options = { ...default_options, ...init_options };
|
||||
|
||||
|
||||
const token = options.use_token ? localStorage.getItem("accessToken") : null;
|
||||
|
||||
const request_options = {
|
||||
...init,
|
||||
headers: {
|
||||
...(init.headers || {}),
|
||||
...(token ? {Authorization: `Bearer ${token}`} : {}),
|
||||
...(init.method && ['PUT', 'POST'].includes(init.method.toUpperCase())
|
||||
? {'Content-Type': 'application/json'}
|
||||
: {}),
|
||||
}
|
||||
};
|
||||
|
||||
if(options.use_cache && ongoingRequests.has(options.cache_key)){
|
||||
return ongoingRequests.get(options.cache_key);
|
||||
}
|
||||
const now = Date.now();
|
||||
const cached_data = localStorage.getItem(options.cache_key);
|
||||
if(options.use_cache && cached_data){
|
||||
const {data, timestamp} = JSON.parse(cached_data);
|
||||
if(now - timestamp < options.cache_expires * 1000)
|
||||
return data;
|
||||
}
|
||||
|
||||
try {
|
||||
const fetchPromise = fetch(url, request_options)
|
||||
.then((response) => {
|
||||
if(!response.ok) {
|
||||
throw new Error(`RESPONSE_ERROR: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
|
||||
if (options.use_cache)
|
||||
{
|
||||
localStorage.setItem(
|
||||
options.cache_key,
|
||||
JSON.stringify({ data, timestamp: now })
|
||||
);
|
||||
ongoingRequests.delete(options.cache_key);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
if(options.use_cache){
|
||||
ongoingRequests.set(options.cache_key, fetchPromise);
|
||||
}
|
||||
return await fetchPromise;
|
||||
}catch(error){
|
||||
if(options.use_cache){
|
||||
ongoingRequests.delete(options.cache_key);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user