Generalized the useLiveify hook
This commit is contained in:
62
client/src/components/liveify.ts
Normal file
62
client/src/components/liveify.ts
Normal 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;
|
||||||
|
}
|
||||||
@@ -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) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user