improve: use react-query for caching

This commit is contained in:
h z
2024-12-08 17:11:14 +00:00
parent a31cec7ef0
commit 0e6fd8409a
15 changed files with 640 additions and 353 deletions

View File

@@ -1,70 +1,60 @@
// src/components/PathManager.js
import React, { useEffect, useState, useRef } from "react";
import { fetch_ } from "../utils/requestUtils";
import config from "../config";
import { useCreatePath, usePaths } from "../utils/path-queries";
import { useQueryClient } from "react-query";
import "./PathManager.css";
import {fetch_} from "../utils/request-utils";
import config from "../config";
const PathManager = ({ currentPathId = 1, onPathChange }) => {
const [currentPath, setCurrentPath] = useState([{ name: "Root", id: 1 }]);
//const [currentPath, setCurrentPath] = useState(buildPath(currentPathId));
const [currentId, setCurrentId] = useState(currentPathId);
const [subPaths, setSubPaths] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [loading, setLoading] = useState(false);
const [dropdownActive, setDropdownActive] = useState(false);
const inputRef = useRef();
const buildPath = async (path_id) => {
const path = [];
let current_id = path_id;
while (current_id) {
const pathData = await fetch_(`${config.BACKEND_HOST}/api/path/${current_id}`, {},{
use_cache: true,
use_token: false,
});
current_id = pathData.parent_id;
path.unshift(pathData);
const queryClient = useQueryClient();
const { data: subPaths, isLoading: isSubPathsLoading, error: subPathsError } = usePaths(currentPathId);
const createPath = useCreatePath();
const buildPath = async (pathId) => {
const path = [];
let current_id = pathId;
while (current_id) {
try {
const pathData = await queryClient.fetchQuery(
["path", current_id],
() => fetch_(`${config.BACKEND_HOST}/api/path/${current_id}`)
);
if (!pathData) break;
path.unshift({ name: pathData.name, id: pathData.id });
current_id = pathData.parent_id;
} catch (error) {
console.error(`Failed to fetch path with id ${current_id}:`, error);
break;
}
}
return path;
}
useEffect(() => {
fetchSubPaths(currentId);
}, [currentId]);
};
useEffect(() => {
const init = async () => {
const pth = await buildPath(currentPathId);
setCurrentPath(pth);
const path = await buildPath(currentPathId);
setCurrentPath(path);
};
init();
}, [currentPathId]);
const fetchSubPaths = (pathId) => {
setLoading(true);
fetch_(`${config.BACKEND_HOST}/api/path/parent/${pathId}`, {}, {
use_cache: false,
use_token: true
})
.then((data) => setSubPaths(data))
.catch((error) => console.error("Failed to fetch subdirectories:", error))
.finally(() => setLoading(false));
};
}, [currentPathId, queryClient]);
const handlePathClick = (pathId, pathIndex) => {
const newPath = currentPath.slice(0, pathIndex + 1);
setCurrentPath(newPath);
setCurrentId(pathId);
onPathChange(pathId);
};
const handleSubPathSelect = (subPath) => {
const updatedPath = [...currentPath, { name: subPath.name, id: subPath.id }];
setCurrentPath(updatedPath);
setCurrentId(subPath.id);
onPathChange(subPath.id);
setSearchTerm("");
setDropdownActive(false);
@@ -75,32 +65,44 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
alert("Directory name cannot be empty.");
return;
}
fetch_(`${config.BACKEND_HOST}/api/path/`, {
method: "POST",
body: JSON.stringify({ name: searchTerm.trim(), parent_id: currentId }),
}, { use_cache: false, use_token: true })
.then((newDir) => {
setSubPaths([...subPaths, newDir]);
setSearchTerm("");
alert("Directory created successfully!");
})
.catch((error) => {
console.error("Failed to create directory:", error);
alert("Failed to create directory.");
});
createPath.mutate(
{ name: searchTerm.trim(), parent_id: currentPathId },
{
onSuccess: (newDir) => {
queryClient.setQueryData(["path", newDir.id], newDir);
queryClient.invalidateQueries(["paths", currentPathId]);
setSearchTerm("");
alert("Directory created successfully.");
},
onError: (error) => {
console.error("Failed to create directory:", error);
alert("Failed to create directory.");
},
}
);
};
const handleInputFocus = () => setDropdownActive(true);
const handleInputBlur = () => {
setTimeout(() => setDropdownActive(false), 150);
};
const filteredSubPaths = subPaths.filter((path) =>
path.name.toLowerCase().includes(searchTerm.toLowerCase())
);
const filteredSubPaths = subPaths
? subPaths.filter((path) =>
path.name.toLowerCase().includes(searchTerm.toLowerCase())
)
: [];
const handleKeyDown = (e) => {
if (e.key === "Enter") {
e.preventDefault();
handleAddDirectory();
}
};
return (
<div className="path-manager">
<div className="path-manager-header">
<div className="current-path">
@@ -129,13 +131,14 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
onChange={(e) => setSearchTerm(e.target.value)}
onFocus={handleInputFocus}
onBlur={handleInputBlur}
onKeyDown={handleKeyDown}
/>
</div>
<div className="control">
<button
className="button is-small is-primary"
onClick={handleAddDirectory}
disabled={loading || !searchTerm.trim()}
disabled={isSubPathsLoading || !searchTerm.trim()}
type="button"
>
Create "{searchTerm}"
@@ -163,6 +166,8 @@ const PathManager = ({ currentPathId = 1, onPathChange }) => {
</div>
</div>
)}
{isSubPathsLoading && <p>Loading...</p>}
{subPathsError && <p>Error loading subdirectories.</p>}
</div>
</div>
);