refactor: remove socket context dependencies and streamline socket handling in components for improved performance and readability
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Box from '@mui/material/Box';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import { Link } from 'react-router-dom';
|
||||
import SocketContext from '../contexts/SocketContext.js';
|
||||
|
||||
|
||||
// @note SwashingtonCP font is now loaded globally via index.css
|
||||
|
||||
@@ -22,7 +22,7 @@ const CategoryBox = ({
|
||||
const [imageUrl, setImageUrl] = useState(null);
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const context = useContext(SocketContext);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let objectUrl = null;
|
||||
@@ -60,11 +60,10 @@ const CategoryBox = ({
|
||||
return;
|
||||
}
|
||||
|
||||
// If socket is available and connected, fetch the image
|
||||
if (context && context.socket /*&& context.socket.connected*/ && id && !isLoading) {
|
||||
if (id && !isLoading) {
|
||||
setIsLoading(true);
|
||||
|
||||
context.socket.emit('getCategoryPic', { categoryId: id }, (response) => {
|
||||
|
||||
window.socketManager.emit('getCategoryPic', { categoryId: id }, (response) => {
|
||||
setIsLoading(false);
|
||||
|
||||
if (response.success) {
|
||||
@@ -119,7 +118,7 @@ const CategoryBox = ({
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
}
|
||||
};
|
||||
}, [context, context?.socket?.connected, id, isLoading]);
|
||||
}, [id, isLoading]);
|
||||
|
||||
return (
|
||||
<Paper
|
||||
|
||||
@@ -224,19 +224,6 @@ class Content extends Component {
|
||||
this.fetchSearchData(this.props.searchParams?.get('q'));
|
||||
})
|
||||
}
|
||||
|
||||
// Handle socket connection changes
|
||||
const wasConnected = prevProps.socket && prevProps.socket.connected;
|
||||
const isNowConnected = this.props.socket && this.props.socket.connected;
|
||||
|
||||
if (!wasConnected && isNowConnected && !this.state.loaded) {
|
||||
// Socket just connected and we haven't loaded data yet, retry loading
|
||||
if (this.props.params.categoryId) {
|
||||
this.fetchCategoryData(this.props.params.categoryId);
|
||||
} else if (this.props.searchParams?.get('q')) {
|
||||
this.fetchSearchData(this.props.searchParams?.get('q'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processData(response) {
|
||||
@@ -278,19 +265,13 @@ class Content extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
//if (!this.props.socket || !this.props.socket.connected) {
|
||||
// Socket not connected yet, but don't show error immediately on first load
|
||||
// The componentDidUpdate will retry when socket connects
|
||||
// console.log("Socket not connected yet, waiting for connection to fetch category data");
|
||||
// return;
|
||||
//}
|
||||
console.log(`productList:${categoryId}`);
|
||||
this.props.socket.off(`productList:${categoryId}`);
|
||||
window.socketManager.off(`productList:${categoryId}`);
|
||||
|
||||
// Track if we've received the full response to ignore stub response if needed
|
||||
let receivedFullResponse = false;
|
||||
|
||||
this.props.socket.on(`productList:${categoryId}`,(response) => {
|
||||
window.socketManager.on(`productList:${categoryId}`,(response) => {
|
||||
console.log("getCategoryProducts full response", response);
|
||||
receivedFullResponse = true;
|
||||
setCachedCategoryData(categoryId, response);
|
||||
@@ -301,7 +282,7 @@ class Content extends Component {
|
||||
}
|
||||
});
|
||||
|
||||
this.props.socket.emit("getCategoryProducts", { categoryId: categoryId },
|
||||
window.socketManager.emit("getCategoryProducts", { categoryId: categoryId },
|
||||
(response) => {
|
||||
console.log("getCategoryProducts stub response", response);
|
||||
// Only process stub response if we haven't received the full response yet
|
||||
@@ -365,14 +346,7 @@ class Content extends Component {
|
||||
}
|
||||
|
||||
fetchSearchData(query) {
|
||||
// if (!this.props.socket || !this.props.socket.connected) {
|
||||
// Socket not connected yet, but don't show error immediately on first load
|
||||
// The componentDidUpdate will retry when socket connects
|
||||
// console.log("Socket not connected yet, waiting for connection to fetch search data");
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.props.socket.emit("getSearchProducts", { query },
|
||||
window.socketManager.emit("getSearchProducts", { query },
|
||||
(response) => {
|
||||
if (response && response.products) {
|
||||
this.processData(response);
|
||||
@@ -734,8 +708,6 @@ class Content extends Component {
|
||||
|
||||
<Box>
|
||||
<ProductList
|
||||
socket={this.props.socket}
|
||||
socketB={this.props.socketB}
|
||||
totalProductCount={(this.state.unfilteredProducts || []).length}
|
||||
products={this.state.filteredProducts || []}
|
||||
activeAttributeFilters={this.state.activeAttributeFilters || []}
|
||||
|
||||
@@ -12,7 +12,7 @@ import LoupeIcon from '@mui/icons-material/Loupe';
|
||||
class Images extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { mainPic:0,pics:[], needsSocketRetry: false };
|
||||
this.state = { mainPic:0,pics:[] };
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
@@ -22,15 +22,6 @@ class Images extends Component {
|
||||
if (prevProps.fullscreenOpen !== this.props.fullscreenOpen) {
|
||||
this.updatePics();
|
||||
}
|
||||
|
||||
// Retry loading images if socket just became available
|
||||
const wasConnected = prevProps.socketB && prevProps.socketB.connected;
|
||||
const isNowConnected = this.props.socketB && this.props.socketB.connected;
|
||||
|
||||
if (!wasConnected && isNowConnected && this.state.needsSocketRetry) {
|
||||
this.setState({ needsSocketRetry: false });
|
||||
this.updatePics();
|
||||
}
|
||||
}
|
||||
|
||||
updatePics = (newMainPic = this.state.mainPic) => {
|
||||
@@ -85,14 +76,9 @@ class Images extends Component {
|
||||
}
|
||||
|
||||
loadPic = (size,bildId,index) => {
|
||||
// Check if socketB is available and connected before emitting
|
||||
if (!this.props.socketB || !this.props.socketB.connected) {
|
||||
console.log("Images: socketB not available, will retry when connected");
|
||||
this.setState({ needsSocketRetry: true });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.props.socketB.emit('getPic', { bildId, size }, (res) => {
|
||||
window.socketManager.emit('getPic', { bildId, size }, (res) => {
|
||||
if(res.success){
|
||||
const url = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' }));
|
||||
|
||||
@@ -116,44 +102,6 @@ class Images extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
// Prerender detection - if no sockets, render simple CardMedia with static path
|
||||
if (!this.props.socketB) {
|
||||
const getImagePath = (pictureList) => {
|
||||
if (!pictureList || !pictureList.trim()) {
|
||||
return '/assets/images/nopicture.jpg';
|
||||
}
|
||||
return `/assets/images/prod${pictureList.split(',')[0].trim()}.jpg`;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box sx={{ position: 'relative', display: 'inline-block', width: '499px', maxWidth: '100%' }}>
|
||||
<CardMedia
|
||||
component="img"
|
||||
height="400"
|
||||
image={getImagePath(this.props.pictureList)}
|
||||
alt={this.props.productName || 'Produktbild'}
|
||||
fetchPriority="high"
|
||||
loading="eager"
|
||||
sx={{
|
||||
objectFit: 'contain',
|
||||
cursor: 'pointer',
|
||||
transition: 'transform 0.2s ease-in-out',
|
||||
width: '499px',
|
||||
maxWidth: '100%',
|
||||
'&:hover': {
|
||||
transform: 'scale(1.02)'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Stack direction="row" spacing={2} sx={{ justifyContent: 'flex-start', mt: 1, mb: 1 }}>
|
||||
{/* Empty thumbnail gallery for prerender - reserves the mt+mb spacing (16px) */}
|
||||
</Stack>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// SPA version - full functionality with static fallback
|
||||
const getImageSrc = () => {
|
||||
// If dynamic image is loaded, use it
|
||||
|
||||
@@ -27,17 +27,8 @@ class Product extends Component {
|
||||
this.state = {image:window.smallPicCache[bildId],loading:false, error: false}
|
||||
}else{
|
||||
this.state = {image: null, loading: true, error: false};
|
||||
console.log("Product: Fetching image from socketB", this.props.socketB);
|
||||
|
||||
// Check if socketB is available and connected before emitting
|
||||
if (this.props.socketB && this.props.socketB.connected) {
|
||||
this.loadImage(bildId);
|
||||
} else {
|
||||
// Socket not available, set error state or wait
|
||||
console.log("Product: socketB not available, will retry when connected");
|
||||
this.state.error = true;
|
||||
this.state.loading = false;
|
||||
}
|
||||
this.loadImage(bildId);
|
||||
}
|
||||
}else{
|
||||
this.state = {image: null, loading: false, error: false};
|
||||
@@ -48,24 +39,9 @@ class Product extends Component {
|
||||
this._isMounted = true;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// Retry loading image if socket just became available
|
||||
const wasConnected = prevProps.socketB && prevProps.socketB.connected;
|
||||
const isNowConnected = this.props.socketB && this.props.socketB.connected;
|
||||
|
||||
if (!wasConnected && isNowConnected && this.state.error && this.props.pictureList) {
|
||||
// Socket just connected and we had an error, retry loading
|
||||
const bildId = this.props.pictureList.split(',')[0];
|
||||
if (!window.smallPicCache[bildId]) {
|
||||
this.setState({loading: true, error: false});
|
||||
this.loadImage(bildId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadImage = (bildId) => {
|
||||
if (this.props.socketB && this.props.socketB.connected) {
|
||||
this.props.socketB.emit('getPic', { bildId, size:'small' }, (res) => {
|
||||
|
||||
window.socketManager.emit('getPic', { bildId, size:'small' }, (res) => {
|
||||
if(res.success){
|
||||
window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' }));
|
||||
if (this._isMounted) {
|
||||
@@ -85,7 +61,7 @@ class Product extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
|
||||
@@ -105,15 +105,6 @@ class ProductDetailPage extends Component {
|
||||
{ product: null, loading: true, error: null, imageDialogOpen: false },
|
||||
this.loadProductData
|
||||
);
|
||||
|
||||
// Handle socket connection changes
|
||||
const wasConnected = prevProps.socket && prevProps.socket.connected;
|
||||
const isNowConnected = this.props.socket && this.props.socket.connected;
|
||||
|
||||
if (!wasConnected && isNowConnected && this.state.loading) {
|
||||
// Socket just connected and we're still loading, retry loading data
|
||||
this.loadProductData();
|
||||
}
|
||||
}
|
||||
|
||||
loadKomponentImage = (komponentId, pictureList) => {
|
||||
@@ -141,14 +132,8 @@ class ProductDetailPage extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if socketB is available
|
||||
if (!this.props.socketB || !this.props.socketB.connected) {
|
||||
console.log("SocketB not connected yet, skipping image load for komponent:", komponentId);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch image from server
|
||||
this.props.socketB.emit('getPic', { bildId, size: 'small' }, (res) => {
|
||||
window.socketManager.emit('getPic', { bildId, size: 'small' }, (res) => {
|
||||
if (res.success) {
|
||||
// Cache the image
|
||||
window.smallPicCache[bildId] = URL.createObjectURL(new Blob([res.imageBuffer], { type: 'image/jpeg' }));
|
||||
@@ -229,12 +214,6 @@ class ProductDetailPage extends Component {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not cached, fetch from server (similar to loadProductData)
|
||||
//if (!this.props.socket || !this.props.socket.connected) {
|
||||
// console.log("Socket not connected yet, waiting for connection to load komponent data");
|
||||
// return;
|
||||
//}
|
||||
|
||||
// Mark this komponent as loading
|
||||
this.setState(prevState => ({
|
||||
komponentenData: {
|
||||
@@ -248,7 +227,7 @@ class ProductDetailPage extends Component {
|
||||
}
|
||||
}));
|
||||
|
||||
this.props.socket.emit(
|
||||
window.socketManager.emit(
|
||||
"getProductView",
|
||||
{ articleId: id },
|
||||
(res) => {
|
||||
@@ -359,13 +338,8 @@ class ProductDetailPage extends Component {
|
||||
}
|
||||
|
||||
loadProductData = () => {
|
||||
if (!this.props.socket || !this.props.socket.connected) {
|
||||
// Socket not connected yet, but don't show error immediately on first load
|
||||
// The componentDidUpdate will retry when socket connects
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.socket.emit(
|
||||
window.socketManager.emit(
|
||||
"getProductView",
|
||||
{ seoName: this.props.seoName },
|
||||
(res) => {
|
||||
@@ -434,8 +408,8 @@ class ProductDetailPage extends Component {
|
||||
}
|
||||
} else {
|
||||
// Not in cache, fetch from server
|
||||
if (this.props.socketB && this.props.socketB.connected) {
|
||||
this.props.socketB.emit(
|
||||
|
||||
window.socketManager.emit(
|
||||
"getAttributePicture",
|
||||
{ id: cacheKey },
|
||||
(res) => {
|
||||
@@ -469,7 +443,7 @@ class ProductDetailPage extends Component {
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,8 +672,6 @@ class ProductDetailPage extends Component {
|
||||
>
|
||||
<ProductImage
|
||||
product={product}
|
||||
socket={this.props.socket}
|
||||
socketB={this.props.socketB}
|
||||
fullscreenOpen={this.state.imageDialogOpen}
|
||||
onOpenFullscreen={this.handleOpenDialog}
|
||||
onCloseFullscreen={this.handleCloseDialog}
|
||||
@@ -1032,7 +1004,6 @@ class ProductDetailPage extends Component {
|
||||
<ArticleQuestionForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
@@ -1043,7 +1014,6 @@ class ProductDetailPage extends Component {
|
||||
<ArticleRatingForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</div>
|
||||
</Collapse>
|
||||
@@ -1054,7 +1024,6 @@ class ProductDetailPage extends Component {
|
||||
<ArticleAvailabilityForm
|
||||
productId={product.id}
|
||||
productName={cleanProductName(product.name)}
|
||||
socket={this.props.socket}
|
||||
/>
|
||||
</Collapse>
|
||||
)}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import React from 'react';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import SocketContext from '../contexts/SocketContext.js';
|
||||
import ProductDetailPage from './ProductDetailPage.js';
|
||||
|
||||
// Wrapper component for individual product detail page with socket
|
||||
const ProductDetailWithSocket = () => {
|
||||
const { seoName } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
return (
|
||||
<SocketContext.Consumer>
|
||||
{({socket,socketB}) => <ProductDetailPage seoName={seoName} navigate={navigate} location={location} socket={socket} socketB={socketB} />}
|
||||
</SocketContext.Consumer>
|
||||
<ProductDetailPage seoName={seoName} navigate={navigate} location={location} />
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { useContext, useEffect, useState } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import ChevronLeft from "@mui/icons-material/ChevronLeft";
|
||||
import ChevronRight from "@mui/icons-material/ChevronRight";
|
||||
import CategoryBox from "./CategoryBox.js";
|
||||
import SocketContext from "../contexts/SocketContext.js";
|
||||
import { useCarousel } from "../contexts/CarouselContext.js";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -62,7 +61,6 @@ const initializeCategories = (language = 'en') => {
|
||||
|
||||
const SharedCarousel = () => {
|
||||
const { carouselRef, filteredCategories, setFilteredCategories, moveCarousel } = useCarousel();
|
||||
const context = useContext(SocketContext);
|
||||
const { t, i18n } = useTranslation();
|
||||
const [rootCategories, setRootCategories] = useState([]);
|
||||
const [currentLanguage, setCurrentLanguage] = useState(i18n.language || 'de');
|
||||
@@ -94,13 +92,11 @@ const SharedCarousel = () => {
|
||||
}, [i18n]);
|
||||
|
||||
useEffect(() => {
|
||||
// Only fetch from socket if we don't already have categories
|
||||
if (
|
||||
rootCategories.length === 0 &&
|
||||
context && context.socket && context.socket.connected &&
|
||||
typeof window !== "undefined"
|
||||
) {
|
||||
context.socket.emit("categoryList", { categoryId: 209, language: currentLanguage, requestTranslation: true }, (response) => {
|
||||
window.socketManager.emit("categoryList", { categoryId: 209, language: currentLanguage, requestTranslation: true }, (response) => {
|
||||
if (response && response.success) {
|
||||
// Use translated data if available, otherwise fall back to original
|
||||
const categoryTreeToUse = response.translation || response.categoryTree;
|
||||
@@ -136,7 +132,7 @@ const SharedCarousel = () => {
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [context, context?.socket?.connected, rootCategories.length, currentLanguage]);
|
||||
}, [rootCategories.length, currentLanguage]);
|
||||
|
||||
useEffect(() => {
|
||||
const filtered = rootCategories.filter(
|
||||
|
||||
@@ -3,32 +3,90 @@ import { io } from 'socket.io-client';
|
||||
|
||||
class SocketManager {
|
||||
constructor() {
|
||||
|
||||
this.socket = io('', {
|
||||
transports: ["websocket"],
|
||||
autoConnect: false
|
||||
});
|
||||
|
||||
this.emit = this.emit.bind(this);
|
||||
this.on = this.on.bind(this);
|
||||
this.off = this.off.bind(this);
|
||||
this.connectPromise = null;
|
||||
this.pendingListeners = new Map();
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
// If socket is already connected, register the listener directly
|
||||
if (this.socket.connected) {
|
||||
this.socket.on(event, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the listener to be registered when connection is established
|
||||
if (!this.pendingListeners.has(event)) {
|
||||
this.pendingListeners.set(event, new Set());
|
||||
}
|
||||
this.pendingListeners.get(event).add(callback);
|
||||
|
||||
// If not already connecting, initiate connection
|
||||
if (!this.connectPromise) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
// Register the listener now, it will receive events once connected
|
||||
this.socket.on(event, callback);
|
||||
}
|
||||
|
||||
off(event, callback) {
|
||||
// Remove from socket listeners
|
||||
this.socket.off(event, callback);
|
||||
|
||||
// Remove from pending listeners if present
|
||||
if (this.pendingListeners.has(event)) {
|
||||
const listeners = this.pendingListeners.get(event);
|
||||
listeners.delete(callback);
|
||||
if (listeners.size === 0) {
|
||||
this.pendingListeners.delete(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connect() {
|
||||
if (this.connectPromise) return this.connectPromise;
|
||||
|
||||
this.connectPromise = new Promise((resolve, reject) => {
|
||||
this.socket.connect();
|
||||
|
||||
this.socket.once('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
|
||||
this.socket.once('connect_error', (error) => {
|
||||
this.connectPromise = null;
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
return this.connectPromise;
|
||||
}
|
||||
|
||||
|
||||
emit(event, ...args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.socket.connected) {
|
||||
// Connect the socket first
|
||||
this.socket.connect();
|
||||
// If not already connecting, start connection
|
||||
if (!this.connectPromise) {
|
||||
this.connect();
|
||||
}
|
||||
|
||||
// Wait for connection before emitting
|
||||
this.socket.once('connect', () => {
|
||||
this.socket.emit(event, ...args);
|
||||
resolve();
|
||||
});
|
||||
|
||||
// Handle connection error
|
||||
this.socket.once('connect_error', (error) => {
|
||||
reject(error);
|
||||
});
|
||||
this.connectPromise
|
||||
.then(() => {
|
||||
this.socket.emit(event, ...args);
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
// Socket already connected, emit directly
|
||||
this.socket.emit(event, ...args);
|
||||
@@ -36,7 +94,6 @@ class SocketManager {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
|
||||
Reference in New Issue
Block a user