Remove memoized pattern

This commit is contained in:
Oliver Walters 2026-06-28 06:06:50 +00:00
parent df00bfb1d3
commit 7e7826b75a
4 changed files with 59 additions and 68 deletions

View File

@ -199,7 +199,7 @@ function ReturnOrderLineItemFormRow({
label: t`Status`,
choices: statusOptions,
onValueChange: (value) => {
props.changeFn(props.idx, 'status', value);
props.changeFn(props.rowId, 'status', value);
}
}}
defaultValue={record.item_detail?.status}
@ -207,7 +207,7 @@ function ReturnOrderLineItemFormRow({
/>
</Table.Td>
<Table.Td>
<RemoveRowButton onClick={() => props.removeFn(props.idx)} />
<RemoveRowButton onClick={() => props.removeFn(props.rowId)} />
</Table.Td>
</Table.Tr>
</>
@ -226,6 +226,7 @@ export function useReceiveReturnOrderLineItems(
field_type: 'table',
value: props.items.map((item: any) => {
return {
id: item.pk,
item: item.pk
};
}),
@ -236,7 +237,7 @@ export function useReceiveReturnOrderLineItems(
<ReturnOrderLineItemFormRow
props={row}
record={record}
key={record.pk}
key={row.rowId}
/>
);
},

View File

@ -26,6 +26,7 @@ import type {
} from '@lib/types/Forms';
import dayjs from 'dayjs';
import type { TableFieldRowProps } from '../components/forms/fields/TableField';
import { useWhyDidYouUpdate } from '../functions/debug';
import useBackgroundTask from '../hooks/UseBackgroundTask';
import { useCreateApiFormModal, useEditApiFormModal } from '../hooks/UseForm';
import { useGlobalSettingsState } from '../states/SettingsStates';
@ -310,6 +311,12 @@ function SalesOrderAllocateLineRow({
record: any;
sourceLocation?: number | null;
}>) {
useWhyDidYouUpdate('SalesOrderAllocateLineRow', {
props,
record,
sourceLocation
});
// Statically defined field for selecting the stock item
const stockItemField: ApiFormFieldType = useMemo(() => {
return {
@ -328,28 +335,33 @@ function SalesOrderAllocateLineRow({
value: props.item.stock_item,
name: 'stock_item',
onValueChange: (value: any, instance: any) => {
props.changeFn(props.idx, 'stock_item', value);
props.changeFn(props.rowId, 'stock_item', value);
// Update the allocated quantity based on the selected stock item
if (instance) {
const available = instance.quantity - instance.allocated;
const required = record.quantity - record.allocated;
let quantity = props.item?.quantity ?? 0;
let q = props.item?.quantity ?? 0;
quantity = Math.max(quantity, required);
quantity = Math.min(quantity, available);
q = Math.max(q, required);
q = Math.min(q, available);
if (quantity != props.item.quantity) {
props.changeFn(props.idx, 'quantity', quantity);
if (q != props.item?.quantity) {
setQuantity(q);
props.changeFn(props.rowId, 'quantity', q);
}
}
}
};
}, [sourceLocation, record, props]);
const [quantity, setQuantity] = useState<number | ''>(
props.item?.quantity ?? ''
);
return (
<Table.Tr key={`table-row-${props.idx}-${record.pk}`}>
<Table.Tr key={`table-row-${props.rowId}-${record.pk}`}>
<Table.Td>
<RenderPartColumn part={record.part_detail} />
</Table.Td>
@ -373,7 +385,7 @@ function SalesOrderAllocateLineRow({
min={0}
step={1}
decimalScale={10}
value={props.item.quantity ?? ''}
value={quantity}
onChange={(value: number | string) => {
let nextValue: number | '' = '';
@ -384,13 +396,14 @@ function SalesOrderAllocateLineRow({
nextValue = Number.isFinite(parsed) ? parsed : '';
}
props.changeFn(props.idx, 'quantity', nextValue);
setQuantity(nextValue);
props.changeFn(props.rowId, 'quantity', nextValue);
}}
error={props.rowErrors?.quantity?.message}
/>
</Table.Td>
<Table.Td>
<RemoveRowButton onClick={() => props.removeFn(props.idx)} />
<RemoveRowButton onClick={() => props.removeFn(props.rowId)} />
</Table.Td>
</Table.Tr>
);
@ -445,7 +458,7 @@ export function useAllocateToSalesOrderForm({
return (
<SalesOrderAllocateLineRow
key={`table-row-${row.idx}-${record.pk}`}
key={row.rowId}
props={row}
record={record}
sourceLocation={sourceLocation}
@ -476,6 +489,7 @@ export function useAllocateToSalesOrderForm({
initialData: {
items: lineItems.map((item) => {
return {
id: item.pk,
line_item: item.pk,
quantity: 0,
stock_item: null

View File

@ -39,7 +39,6 @@ import dayjs from 'dayjs';
import {
type JSX,
Suspense,
memo,
useCallback,
useEffect,
useMemo,
@ -57,6 +56,7 @@ import {
import { Thumbnail } from '../components/images/Thumbnail';
import { StatusRenderer } from '../components/render/StatusRenderer';
import { RenderStockLocation } from '../components/render/Stock';
import { useWhyDidYouUpdate } from '../functions/debug';
import { InvenTreeIcon } from '../functions/icons';
import {
useApiFormModal,
@ -636,6 +636,18 @@ function StockOperationsRow({
}) {
const rowId = props.rowId;
useWhyDidYouUpdate('StockOperationsRow', {
props,
transfer,
changeStatus,
add,
setMax,
merge,
transferMerge,
returnStock,
record
});
const statusOptions: ApiFormFieldChoice[] = useMemo(() => {
return (
StatusFilterOptions(ModelType.stockitem)()?.map((choice) => {
@ -878,39 +890,6 @@ function StockOperationsRow({
);
}
// Memoize each stock operations row, so that we don't re-render the entire table when a single row is updated
const MemoizedStockOperationsRow = memo(
StockOperationsRow,
(previousProps, nextProps) => {
const prevItem = previousProps.props.item;
const nextItem = nextProps.props.item;
const itemUnchanged =
prevItem === nextItem ||
(prevItem?.pk === nextItem?.pk &&
prevItem?.quantity === nextItem?.quantity &&
prevItem?.status === nextItem?.status &&
prevItem?.packaging === nextItem?.packaging &&
prevItem?.merge === nextItem?.merge);
return (
previousProps.props.rowId === nextProps.props.rowId &&
itemUnchanged &&
previousProps.props.rowErrors === nextProps.props.rowErrors &&
previousProps.props.changeFn === nextProps.props.changeFn &&
previousProps.props.removeFn === nextProps.props.removeFn &&
previousProps.record === nextProps.record &&
previousProps.transfer === nextProps.transfer &&
previousProps.changeStatus === nextProps.changeStatus &&
previousProps.add === nextProps.add &&
previousProps.setMax === nextProps.setMax &&
previousProps.merge === nextProps.merge &&
previousProps.transferMerge === nextProps.transferMerge &&
previousProps.returnStock === nextProps.returnStock
);
}
);
type StockItemQuantity = number | '' | undefined;
type StockAdjustmentItem = {
@ -959,7 +938,7 @@ function stockTransferFields(
const record = records[row.item.pk];
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
transfer
changeStatus
@ -1008,7 +987,7 @@ function stockReturnFields(items: any[]): ApiFormFieldSet {
const record = records[row.item.pk];
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
key={record.pk}
record={record}
@ -1074,7 +1053,7 @@ function stockRemoveFields(items: any[]): ApiFormFieldSet {
const record = records[row.item.pk];
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
setMax
changeStatus
@ -1121,11 +1100,11 @@ function stockAddFields(items: any[]): ApiFormFieldSet {
const record = records[row.item.pk];
return (
<MemoizedStockOperationsRow
<StockOperationsRow
changeStatus
props={row}
add
key={record.pk}
key={row.rowId}
record={record}
/>
);
@ -1146,16 +1125,14 @@ function stockAddFields(items: any[]): ApiFormFieldSet {
}
function stockCountFields(items: any[]): ApiFormFieldSet {
if (!items) {
return {};
}
const records = Object.fromEntries(
items?.map((item) => [item.pk, item]) ?? []
);
const records = Object.fromEntries(items.map((item) => [item.pk, item]));
const initialValue = mapAdjustmentItems(items);
const initialValue = items ? mapAdjustmentItems(items) : [];
// Extract all location values from the items
const locations = [...new Set(items.map((item) => item.location))];
const locations = [...new Set(items?.map((item) => item.location))];
const fields: ApiFormFieldSet = {
items: {
@ -1163,11 +1140,11 @@ function stockCountFields(items: any[]): ApiFormFieldSet {
value: initialValue,
modelRenderer: (row: TableFieldRowProps) => {
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
changeStatus
key={row.item.pk}
record={records[row.item.pk]}
key={row.rowId}
record={records[row.item?.pk]}
/>
);
},
@ -1212,7 +1189,7 @@ function stockChangeStatusFields(items: any[]): ApiFormFieldSet {
}),
modelRenderer: (row: TableFieldRowProps) => {
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
key={row.item}
merge
@ -1279,7 +1256,7 @@ function stockMergeFields(items: any[]): ApiFormFieldSet {
}),
modelRenderer: (row: TableFieldRowProps) => {
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
key={row.item.item}
merge
@ -1328,7 +1305,7 @@ function stockAssignFields(items: any[]): ApiFormFieldSet {
}),
modelRenderer: (row: TableFieldRowProps) => {
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
key={row.item.item}
merge
@ -1372,7 +1349,7 @@ function stockDeleteFields(items: any[]): ApiFormFieldSet {
const record = records[row.item];
return (
<MemoizedStockOperationsRow
<StockOperationsRow
props={row}
key={record.pk}
merge

View File

@ -227,7 +227,6 @@ export function useAllocateToTransferOrderForm({
// Memoize the line items to prevent re-rendering
const lines = useMemo(() => {
return lineItems.map((item) => {
console.log('line:', item);
return {
id: item.pk,
...item