Uppy: File doesn't added if add 'file-added' listener calling a dispatch function

Created on 17 Aug 2020  路  4Comments  路  Source: transloadit/uppy

Hi,

I'm using Uppy with react, typescript and redux, and i'm with bug when try to call a dispatch function in 'file-added' listener like:

uppy.on('file-added', file => dispatch(action(file)))

The file selected is added in redux through this dispatch called, but Uppy doesn't add file.
If I remove this listener or comment the dispatch or to change by console.log, works fine, like: uppy.on('file-added', file => console.log(file)).

No error or warnings are appears.

The sandbox to reprodution: https://codesandbox.io/s/gracious-lamarr-t7ohr?file=/src/containers/Home/index.tsx

Question React

Most helpful comment

The issue is here:

const FileUploader = () => {
  const dispatch = useDispatch();
  const uppy = Uppy();
  uppy.on("file-added", (file) => {
    dispatch(actions.addFile(file));
    // console.log(file);
  });

  return <Dashboard uppy={uppy} hideUploadButton />;
}

This code is creating a fresh, empty Uppy instance on each rerender. When Uppy just adds files internally, the React component doesn't actually rerender: Uppy manages that part of the DOM by itself. But, when you dispatch an action, it eventually triggers a React rerender.

The solution is to make sure you're using the same Uppy instance across rerenders. We have some explanation about that here: https://uppy.io/docs/react/initializing/#Functional-Components

In your case, the code would look something like:

const FileUploader = () => {
  const dispatch = useDispatch();
  // useMemo() with `[]` as the dependencies array ensures Uppy is only initialized once when the FileUploader component is created.
  const uppy = useMemo(() => {
    return new Uppy()
      .on("file-added", (file) => {
        dispatch(actions.addFile(file));
        // console.log(file);
      });
  }, []);

  // Clean up event handlers etc when the component unmounts.
  useEffect(() => {
    return () => {
      uppy.close();
    };
  }, []);

  return <Dashboard uppy={uppy} hideUploadButton />;
}

All 4 comments

The issue is here:

const FileUploader = () => {
  const dispatch = useDispatch();
  const uppy = Uppy();
  uppy.on("file-added", (file) => {
    dispatch(actions.addFile(file));
    // console.log(file);
  });

  return <Dashboard uppy={uppy} hideUploadButton />;
}

This code is creating a fresh, empty Uppy instance on each rerender. When Uppy just adds files internally, the React component doesn't actually rerender: Uppy manages that part of the DOM by itself. But, when you dispatch an action, it eventually triggers a React rerender.

The solution is to make sure you're using the same Uppy instance across rerenders. We have some explanation about that here: https://uppy.io/docs/react/initializing/#Functional-Components

In your case, the code would look something like:

const FileUploader = () => {
  const dispatch = useDispatch();
  // useMemo() with `[]` as the dependencies array ensures Uppy is only initialized once when the FileUploader component is created.
  const uppy = useMemo(() => {
    return new Uppy()
      .on("file-added", (file) => {
        dispatch(actions.addFile(file));
        // console.log(file);
      });
  }, []);

  // Clean up event handlers etc when the component unmounts.
  useEffect(() => {
    return () => {
      uppy.close();
    };
  }, []);

  return <Dashboard uppy={uppy} hideUploadButton />;
}

Hi, thanks for your response.

But I yet have the same problem. I using as your said (I updated the codesandbox), and I tryed using as a hook, like documentation suggests here https://uppy.io/docs/react/initializing/, but the behavior keep on, in all cases the uppy didn't add the file in your instance.

Hi, @goto-bus-stop , do you have another idea on how to solve the problem?

Sorry, I copied the code wrong :facepalm: That useEffect() call needs an empty dependency array [] in its second parameter, otherwise it calls .close() on _every_ rerender, which is obviously not good :sweat_smile:

  // Clean up event handlers etc when the component unmounts.
  useEffect(() => {
    return () => {
      uppy.close();
    };
  }, []);
Was this page helpful?
0 / 5 - 0 ratings