React hook: how to use callback in setState

All we need is an easy explanation of the problem, so here it is.

setDropzoneFiles is done, then I need to execute setDropzoneIsLoading. In react class, I can assign callback to setState(), how do I achieve the same with the following code?

const Dropzone = () => {

    const [dropzoneFiles, setDropzoneFiles] = useState([]);
    const [dropzoneIsLoading, setDropzoneIsLoading] = useState(false);

    const onDropAccepted = useCallback(
        acceptedFiles => {
            let someFiles = ['a', 'b'];

            // How to use callback?
            setDropzoneFiles(someFiles, () => {
                setDropzoneIsLoading(true)
            })

        }
    );


    const {getRootProps, getInputProps} = useDropzone({
        onDropAccepted,
        onDropRejected,
        multiple: true
    });
    }

export default Dropzone;

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

The answers you will find online will point you to using useEffect, but this is simply a coverup for a bigger underlying issue.

As mentioned in this post by Lenz Weber as to why there is no callback in useState():

Because it invokes a thought pattern that may very well be an antipattern with hooks.

With hooks, you are writing code more declarative than imparative.

I recommend reading the whole thread as there is great insight in the discussion.

What to do instead?

If you use Redux’s useReducer and dispatch: through a Reducer, you can, in one operation, set the dropZoneFiles and isLoading to true without being interrupted in the middle.

A lot of folks are not fond of Redux, but it exists precisely for this kind of situation.

Also, Dan Abramov from Facebook (big promoter of hooks) himself has frequently mentioned (here for example) that not every component should be immediately migrated to hooks, especially because best-practices need to solidify; I myself prefer Class Components when dealing with complex components.

Method 2

You can use the useEffect hook:

const Dropzone = () => {

    const [dropzoneFiles, setDropzoneFiles] = useState([]);
    const [dropzoneIsLoading, setDropzoneIsLoading] = useState(false);

    const onDropAccepted = useCallback(
        acceptedFiles => {
            let someFiles = ['a', 'b'];

            // How to use callback?
            setDropzoneFiles(someFiles);

        }
    );

    useEffect(() => {
       if (dropzoneFiles) {
          setDropzoneIsLoading(true);
       }

    }, [dropzoneFiles])


    const {getRootProps, getInputProps} = useDropzone({
        onDropAccepted,
        onDropRejected,
        multiple: true
    });
    }

export default Dropzone;

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply