feat(GenericNode): add debounced validation of node on change to improve performance and user experience

feat(utils.ts): add debounce function to debounce function calls
This commit is contained in:
Gabriel Almeida 2023-05-02 16:54:40 -03:00
commit ed5fd7f862
2 changed files with 208 additions and 185 deletions

View file

@ -14,6 +14,7 @@ import { PopUpContext } from "../../contexts/popUpContext";
import NodeModal from "../../modals/NodeModal"; import NodeModal from "../../modals/NodeModal";
import { useCallback } from "react"; import { useCallback } from "react";
import { TabsContext } from "../../contexts/tabsContext"; import { TabsContext } from "../../contexts/tabsContext";
import { debounce } from "../../utils";
export default function GenericNode({ export default function GenericNode({
data, data,
selected, selected,
@ -29,7 +30,7 @@ export default function GenericNode({
const [validationStatus, setValidationStatus] = useState("idle"); const [validationStatus, setValidationStatus] = useState("idle");
// State for outline color // State for outline color
const [isValid, setIsValid] = useState(false); const [isValid, setIsValid] = useState(false);
const {save} = useContext(TabsContext) const { save } = useContext(TabsContext);
const { reactFlowInstance } = useContext(typesContext); const { reactFlowInstance } = useContext(typesContext);
const [params, setParams] = useState([]); const [params, setParams] = useState([]);
@ -41,15 +42,17 @@ export default function GenericNode({
} }
}, [save]); }, [save]);
useEffect(() => { const validateNode = useCallback(
debounce(async () => {
try { try {
fetch(`/validate/node/${data.id}`, { const response = await fetch(`/validate/node/${data.id}`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify(reactFlowInstance.toObject()), body: JSON.stringify(reactFlowInstance.toObject()),
}).then((response) => { });
console.log(response.status, response.body); console.log(response.status, response.body);
if (response.status === 200) { if (response.status === 200) {
@ -57,12 +60,18 @@ export default function GenericNode({
} else if (response.status === 500) { } else if (response.status === 500) {
setValidationStatus("error"); setValidationStatus("error");
} }
});
} catch (error) { } catch (error) {
console.error("Error validating node:", error); console.error("Error validating node:", error);
setValidationStatus("error"); setValidationStatus("error");
} }
}, [params]); }, 1000), // Adjust the debounce delay (500ms) as needed
[reactFlowInstance, data.id]
);
useEffect(() => {
if (params.length > 0) {
validateNode();
}
}, [params, validateNode]);
useEffect(() => { useEffect(() => {
if (validationStatus === "success") { if (validationStatus === "success") {
@ -123,7 +132,8 @@ export default function GenericNode({
<Cog6ToothIcon <Cog6ToothIcon
className={classNames( className={classNames(
Object.keys(data.node.template).some( Object.keys(data.node.template).some(
(t) => data.node.template[t].advanced && data.node.template[t].show (t) =>
data.node.template[t].advanced && data.node.template[t].show
) )
? "" ? ""
: "hidden", : "hidden",
@ -170,7 +180,8 @@ export default function GenericNode({
) : ( ) : (
<></> <></>
)} */} )} */}
{data.node.template[t].show && !data.node.template[t].advanced ? ( {data.node.template[t].show &&
!data.node.template[t].advanced ? (
<ParameterComponent <ParameterComponent
data={data} data={data}
color={ color={

View file

@ -399,8 +399,11 @@ export function removeApiKeys(flow: FlowType): FlowType {
return cleanFLow; return cleanFLow;
} }
export function updateObject<T extends Record<string, any>>(reference: T, objectToUpdate: T): T { export function updateObject<T extends Record<string, any>>(
let clonedObject = _.cloneDeep(objectToUpdate) reference: T,
objectToUpdate: T
): T {
let clonedObject = _.cloneDeep(objectToUpdate);
// Loop through each key in the object to update // Loop through each key in the object to update
for (const key in clonedObject) { for (const key in clonedObject) {
// If the key is not in the reference object, delete it // If the key is not in the reference object, delete it
@ -417,3 +420,12 @@ export function updateObject<T extends Record<string, any>>(reference: T, object
} }
return clonedObject; return clonedObject;
} }
export function debounce(func, wait) {
let timeout;
return function (...args) {
const context = this;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}