Repository: onlook-dev/onlook
File: apps/web/client/src/app/project/[id]/_components/right-panel/dev-tab/index.tsx (TypeScript)

Code before:
    // Subscribe to file events
    useEffect(() => {
        const handleFileEvent = async (event: FileEvent) => {
            // Only fetch all files when files are added/removed
            if (event.type === 'add' || event.type === 'remove') {
                ide.isFilesLoading = true;
                try {
                    await ide.refreshFiles();
                } catch (error) {
                    console.error('Error loading files:', error);
                }
            }

            if (event.type === 'change') {
                if (ide.activeFile) {
                    if (event.paths.includes(ide.activeFile.path)) {
                        await loadNewContent(ide.activeFile.path);
                    }
                }
            }
        };

        const unsubscribe = editorEngine.sandbox.fileEventBus.subscribe('*', handleFileEvent);

        // Use the subscription cleanup hook
        addSubscription(unsubscribe);
        
    }, [editorEngine.sandbox, ide.activeFile, addSubscription]);

    // Load files when sandbox becomes connected
    useEffect(() => {
        const loadInitialFiles = async () => {
            // Only load files if sandbox is connected and not connecting
            if (!isSandboxReady()) {
                return;
            }

            ide.isFilesLoading = true;
            try {
                await ide.refreshFiles();
            } catch (error) {
                console.error('Error loading initial files:', error);
            }
        };

        loadInitialFiles();
    }, [editorEngine.sandbox.session.session, editorEngine.sandbox.session.isConnecting]);

    // Clear files and opened files when sandbox disconnects
    useEffect(() => {
        if (!isSandboxReady()) {

Review comment (performance):
This will add the subscription multiple time on re-render and create memory leak. We need to dedup or something. 

But taking a step back on this implementation. This is applying a complex solution onto an issue introduced by complexity instead of removing the complexity. 

Why is cleaning this on un-render causing weird behavior? The listeners should not be persistent for the lifetime of the app. If the component is un-rendered, it should stop listening to events. Why is it even subscribing and not using MobX to listen to state changes? Why is there even an eventbus when we already have a subscription pattern with MobX?

Code after:
            // Only fetch all files when files are added/removed
            if (event.type === 'add' || event.type === 'remove') {
                ide.isFilesLoading = true;
                try {
                    await ide.refreshFiles();
                } catch (error) {
                    console.error('Error loading files:', error);
                }
            }

            if (event.type === 'change') {
                if (ide.activeFile) {
                    if (event.paths.includes(ide.activeFile.path)) {
                        await loadNewContent(ide.activeFile.path);
                    }
                }
            }
        };

        const unsubscribe = editorEngine.sandbox.fileEventBus.subscribe('*', handleFileEvent);

        // Use the subscription cleanup hook
        addSubscription('dev-tab', unsubscribe);

    }, [editorEngine.sandbox, ide.activeFile, addSubscription]);

    // Load files when sandbox becomes connected
    useEffect(() => {
        const loadInitialFiles = async () => {
            // Only load files if sandbox is connected and not connecting
            if (!isSandboxReady()) {
                return;
            }

            ide.isFilesLoading = true;
            try {
                await ide.refreshFiles();
            } catch (error) {
                console.error('Error loading initial files:', error);
            }
        };

        loadInitialFiles();
    }, [editorEngine.sandbox.session.session, editorEngine.sandbox.session.isConnecting]);

    // Clear files and opened files when sandbox disconnects
    useEffect(() => {
        if (!isSandboxReady()) {
            ide.clear();
            // Clean up all editor instances
            editorViewsRef.current.forEach((view) => view.destroy());