I have a construction in my project where I will access the states as a model.
For example, in the products model, I define all the functions, pull, save, update and delete to the database, and there are functions that I make these functions for state changes.
For example, when user A deletes a product, I transfer the product information to user B via SignalR. The information received by user B is transferred to the product model and that product is deleted from the state. In this way, instant data tracking is provided.
I define the SignalR signals related to the product in the useEffect section of the product model, the functions that update the other state are defined with useCallback.
The problem is, every time a signal comes in state is updated twice and sometimes it goes into a long loop and crashes the browser. Where is my mistake in the code block I shared below?
Thanks.
const [products, setProducts] = useState<API.ProductListDto[]>([]);
const { initialState } = useModel('@@initialState');
const { signalRService, signalRConnectionId } = initialState || {};
const getProductById = useCallback(
(
productId: string | undefined,
): API.ModelStateFunction<API.ProductDto | undefined> | undefined => {
if (products && products.length > 0) {
let product: API.ProductDto | undefined;
let index: number | undefined;
products.some((e) => {
product = e.Products.find((c) => {
return c.ProductId === productId;
});
if (product) return true;
return false;
});
products.some((e) => {
index = e.Products.findIndex((c) => {
return c.ProductId === productId;
});
console.log(index);
if (index !== -1) return true;
return false;
});
return {
entity: product,
index,
};
}
return undefined;
},
[products],
);
const getProductCategoryById = useCallback(
(
categoryId: string | undefined,
): API.ModelStateFunction<API.ProductListDto | undefined> | undefined => {
if (products && products.length > 0) {
return {
entity: products.find((x) => x.ProductCategoryId === categoryId),
index: products.findIndex((x) => x.ProductCategoryId === categoryId),
};
}
return undefined;
},
[products],
);
const updateProductState = useCallback(
(product: API.ProductDto) => {
if (products && products.length > 0) {
const cloneProdutcs = cloneDeep(products);
const stateProduct = getProductById(product.ProductId);
if (stateProduct && stateProduct.entity) {
const stateProductCategory = getProductCategoryById(
stateProduct.entity.ProductCategoryId,
);
if (stateProductCategory && stateProductCategory.entity) {
if (
stateProduct.entity.ProductCategoryId === product.ProductCategoryId
) {
cloneProdutcs[stateProductCategory.index!].Products[stateProduct.index!] =
product;
} else {
const newProductCategory = getProductCategoryById(product.ProductCategoryId);
if (newProductCategory && newProductCategory.entity) {
cloneProdutcs[stateProductCategory.index!].Products = [
...cloneProdutcs[stateProductCategory.index!].Products.filter(
(x) => x.ProductId !== product.ProductId,
),
];
cloneProdutcs[newProductCategory.index!].Products = [
...cloneProdutcs[newProductCategory.index!].Products,
product,
];
}
}
}
} else {
const category = getProductCategoryById(product.ProductCategoryId);
if (category && category.entity) {
cloneProdutcs[category.index!].Products = [
...cloneProdutcs[category.index!].Products,
product,
];
}
}
setProducts(cloneProdutcs);
}
},
[getProductById, getProductCategoryById, products],
);
const updateCategoryState = useCallback(
(category: API.ProductCategoryDto) => {
if (products && products.length > 0) {
const cloneCategories = cloneDeep(products);
const stateCategory = getProductCategoryById(category.ProductCategoryId);
if (stateCategory && stateCategory.entity) {
cloneCategories[stateCategory.index!] = {
...cloneCategories[stateCategory.index!],
...category,
};
} else {
cloneCategories.push({ ...category, Products: [] });
setProducts(cloneCategories);
}
setProducts(cloneCategories);
}
},
[products, getProductCategoryById],
);
const deleteProductFromState = useCallback(
(productId: string) => {
if (products && products.length > 0) {
const cloneProducts = cloneDeep(products);
const stateProduct = getProductById(productId);
if (stateProduct && stateProduct.entity) {
const stateProductCategory = getProductCategoryById(
stateProduct.entity.ProductCategoryId,
);
if (stateProductCategory && stateProductCategory.entity) {
remove(cloneProducts[stateProductCategory.index!].Products, {
ProductId: productId,
});
setProducts(cloneProducts);
}
}
}
},
[products, getProductById, getProductCategoryById],
);
const deleteProductCategoryFromState = useCallback(
(categoryId: string) => {
if (products && products.length > 0) {
const cloneProducts = cloneDeep(products);
const stateCategory = getProductCategoryById(categoryId);
if (stateCategory && stateCategory.entity) {
remove(cloneProducts, {
ProductCategoryId: categoryId,
});
setProducts(cloneProducts);
}
}
},
[products, getProductCategoryById],
);
useEffect(() => {
function defineSignal() {
if (signalRService) {
if (signalRService.isConnected()) {
if (!isEmpty(signalRConnectionId)) {
signalRService.define(
'Product',
(signalEntity: API.ProductDto, connectionId: string) => {
if (signalRConnectionId !== connectionId) {
updateProductState(signalEntity);
}
},
);
signalRService.define(
'ProductCategory',
(signalEntity: API.ProductCategoryDto, connectionId: string) => {
if (signalRConnectionId !== connectionId) {
updateCategoryState(signalEntity);
}
},
);
signalRService.define(
'DeleteProduct',
(signalEntity: string, connectionId: string) => {
if (signalRConnectionId !== connectionId) {
deleteProductFromState(signalEntity);
}
},
);
signalRService.define(
'DeleteProductCategory',
(signalEntity: string, connectionId: string) => {
if (signalRConnectionId !== connectionId) {
deleteProductCategoryFromState(signalEntity);
}
},
);
}
}
}
}
defineSignal();
}, [
signalRService,
signalRConnectionId,
updateProductState,
updateCategoryState,
deleteProductFromState,
deleteProductCategoryFromState,
]);