react icon indicating copy to clipboard operation
react copied to clipboard

issue with `useCallback` and `useRef` Cleanup in `useEffect` on Production Build with Next.js

Open EhsanKey opened this issue 1 year ago • 1 comments

We are encountering an issue where a useCallback function, managed with useRef, does not perform cleanup as expected during component unmount in both development and production builds of a Next.js application.

Problematic Code

import React, { useCallback, useEffect, useRef } from 'react';

const MyComponent = () => {
  const stopRecordingRef = useRef<() => void>(() => {});

  const [recorder, setRecorder] = React.useState<MediaRecorder | null>(null);
  const [stream, setStream] = React.useState<MediaStream | null>(null);
  const videoRefs = useRef<{ webcam: HTMLVideoElement | null, recordedVideo: HTMLVideoElement | null }>({
    webcam: null,
    recordedVideo: null,
  });

  const stopRecording = useCallback(() => {
    console.log('stopRecording called');
    if (recorder) {
      console.log('Stopping recorder');
      recorder.stop();
    }

    if (stream) {
      console.log('Stopping stream tracks');
      stream.getTracks().forEach(track => track.stop());
      setStream(null);
    }

    if (videoRefs.current?.webcam) {
      console.log('Clearing webcam srcObject');
      videoRefs.current.webcam.srcObject = null;
    }

    if (videoRefs.current?.recordedVideo) {
      console.log('Clearing recorded video src');
      videoRefs.current.recordedVideo.src = '';
    }
  }, [recorder, stream]);

  useEffect(() => {
    stopRecordingRef.current = stopRecording;
  }, [stopRecording]);

  useEffect(() => {
    return () => {
      console.log('Cleaning up in useEffect');
      if (stopRecordingRef.current) {
        stopRecordingRef.current();
      } else {
        console.log('stopRecordingRef.current is not set');
      }
    };
  }, []);

  return (
    <div>
      {/*JSX */}
    </div>
  );
};

export default MyComponent;

Expected Behavior

The stopRecording function should be executed properly during component unmount via useEffect. This function should correctly perform all cleanup operations both in development and production builds.

Actual Behavior

  • In Development: The stopRecording function executes correctly, and cleanup operations are performed as expected.
  • In Production Build: The stopRecording function does not execute properly during component unmount, resulting in lingering resources and incorrect references.

Steps to Reproduce

  1. Implement the stopRecording function using useCallback.
  2. Use useRef to maintain a reference to the stopRecording function.
  3. Set the reference in useEffect.
  4. Verify the cleanup process in the production build.

Environment

  • Next.js Version: 12.0.8
  • React Version: 17.0.2
  • React DOM Version: 17.0.2

Suggestions and Requests

Please investigate this issue and suggest a resolution if available. If a specific solution is not possible, guidance on correctly implementing resource cleanup during component unmounting using useCallback and useRef in a Next.js environment would be highly appreciated.

EhsanKey avatar Aug 08 '24 09:08 EhsanKey

Can you reproduce it ?

hungcung2409 avatar Aug 09 '24 07:08 hungcung2409

This issue has been automatically marked as stale. If this issue is still affecting you, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize it yet. If you have any new additional information, please include it with your comment!

github-actions[bot] avatar Nov 07 '24 08:11 github-actions[bot]

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

github-actions[bot] avatar Nov 14 '24 08:11 github-actions[bot]