Generalized the useLiveify hook

This commit is contained in:
Donkie
2023-10-15 21:20:56 +02:00
parent f7faa6ccdc
commit 37ae1f7de1
2 changed files with 68 additions and 59 deletions

View File

@@ -0,0 +1,62 @@
import { LiveEvent } from "@refinedev/core";
import React from "react";
import liveProvider from "./liveProvider";
const liveProviderInstance = liveProvider(import.meta.env.VITE_APIURL);
/**
* Hook that subscribes to live updates for the items in the dataSource
* @param dataSource Original dataSource
* @returns dataSource that is updated with live data
*/
export function useLiveify<Data extends { id: number }>(
resource: string,
dataSource: Data[],
// eslint-disable-next-line @typescript-eslint/no-explicit-any
transformPayload: (payload: any) => Data
) {
// TODO: The hooks in this function is quite janky, and should be refactored to be more efficient
// New state that holds the dataSource with updated values from the live provider
const [updatedDataSource, setUpdatedDataSource] = React.useState<Data[]>(dataSource);
// If the original dataSource changes, update the updatedDataSource
React.useEffect(() => {
setUpdatedDataSource(dataSource);
}, [dataSource]);
// Create a constant reference to itemIds. This is to prevent the useEffect below from triggering extra times.
const itemIds = dataSource.map((item) => item.id);
const [prevItemIds, setPrevItemIds] = React.useState<number[]>(itemIds);
if (JSON.stringify(itemIds) !== JSON.stringify(prevItemIds)) {
setPrevItemIds(itemIds);
}
// Subscribe to changes for all items in the dataSource
React.useEffect(() => {
const subscription = liveProviderInstance?.subscribe({
channel: `${resource}-list`,
params: {
resource: resource,
ids: prevItemIds,
subscriptionType: "useList",
},
types: ["update"],
callback: (event: LiveEvent) => {
setUpdatedDataSource((prev) =>
prev.map((item) => {
return item.id === event.payload.data.id ? transformPayload(event.payload.data) : item;
})
);
},
});
// Unsubscribe when the component unmounts
return () => {
if (subscription) {
liveProviderInstance?.unsubscribe(subscription);
}
};
}, [resource, prevItemIds, transformPayload]);
return updatedDataSource;
}

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { IResourceComponentsProps, LiveEvent, useInvalidate, useTranslate } from "@refinedev/core"; import { IResourceComponentsProps, useInvalidate, useTranslate } from "@refinedev/core";
import { useTable, List, EditButton, ShowButton, CloneButton } from "@refinedev/antd"; import { useTable, List, EditButton, ShowButton, CloneButton } from "@refinedev/antd";
import { Table, Space, Button, Dropdown, Modal } from "antd"; import { Table, Space, Button, Dropdown, Modal } from "antd";
import dayjs from "dayjs"; import dayjs from "dayjs";
@@ -23,7 +23,7 @@ import {
useSpoolmanLotNumbers, useSpoolmanLotNumbers,
useSpoolmanMaterials, useSpoolmanMaterials,
} from "../../components/otherModels"; } from "../../components/otherModels";
import liveProvider from "../../components/liveProvider"; import { useLiveify } from "../../components/liveify";
dayjs.extend(utc); dayjs.extend(utc);
@@ -59,61 +59,6 @@ function translateColumnI18nKey(columnName: string): string {
const namespace = "spoolList-v2"; const namespace = "spoolList-v2";
const liveProviderInstance = liveProvider(import.meta.env.VITE_APIURL);
/**
* Hook that subscribes to live updates for the spools in the dataSource
* @param dataSource Original dataSource
* @returns dataSource that is updated with live data
*/
function useLiveify(dataSource: ISpoolCollapsed[]) {
// TODO: The hooks in this function is quite janky, and should be refactored to be more efficient
// New state that holds the dataSource with updated values from the live provider
const [updatedDataSource, setUpdatedDataSource] = React.useState<ISpoolCollapsed[]>(dataSource);
// If the original dataSource changes, update the updatedDataSource
React.useEffect(() => {
setUpdatedDataSource(dataSource);
}, [dataSource]);
// Create a constant reference to itemIds. This is to prevent the useEffect below from triggering extra times.
const itemIds = dataSource.map((item) => item.id);
const [prevItemIds, setPrevItemIds] = React.useState<number[]>(itemIds);
if (JSON.stringify(itemIds) !== JSON.stringify(prevItemIds)) {
setPrevItemIds(itemIds);
}
// Subscribe to changes for all items in the dataSource
React.useEffect(() => {
const subscription = liveProviderInstance?.subscribe({
channel: "spool-list",
params: {
resource: "spool",
ids: prevItemIds,
subscriptionType: "useList",
},
types: ["update"],
callback: (event: LiveEvent) => {
const data = event.payload.data as ISpool;
setUpdatedDataSource((prev) =>
prev.map((item) => {
return item.id === data.id ? collapseSpool(data) : item;
})
);
},
});
// Unsubscribe when the component unmounts
return () => {
if (subscription) {
liveProviderInstance?.unsubscribe(subscription);
}
};
}, [prevItemIds]);
return updatedDataSource;
}
export const SpoolList: React.FC<IResourceComponentsProps> = () => { export const SpoolList: React.FC<IResourceComponentsProps> = () => {
const t = useTranslate(); const t = useTranslate();
const invalidate = useInvalidate(); const invalidate = useInvalidate();
@@ -191,9 +136,11 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
useStoreInitialState(namespace, tableState); useStoreInitialState(namespace, tableState);
// Collapse the dataSource to a mutable list and add a filament_name field // Collapse the dataSource to a mutable list and add a filament_name field
const dataSource: ISpoolCollapsed[] = useLiveify( const queryDataSource = React.useMemo(
React.useMemo(() => (tableProps.dataSource ?? []).map(collapseSpool), [tableProps.dataSource]) () => (tableProps.dataSource ?? []).map(collapseSpool),
[tableProps.dataSource]
); );
const dataSource = useLiveify("spool", queryDataSource, collapseSpool);
// Function for opening an ant design modal that asks for confirmation for archiving a spool // Function for opening an ant design modal that asks for confirmation for archiving a spool
const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => { const archiveSpool = async (spool: ISpoolCollapsed, archive: boolean) => {