How to upload multiple images?

Hi. I’m trying to implement an image loader with the ability to insert multiple images. My code is based on the image loader example. The problem: if I try to load for example 3 files, there will appear 3 placeholders, but only one placeholder will be replaced with the image and there is an error - RangeError: Applying a mismatched transaction. My guess is that pos of all placeholders are the same value, but I can’t figure out how to update. This is the code of my insertImage function.

 _pasteImagesFromDisk = () => {
    const files = this._fileInputRef.current.files;
    const view = this._editorView;
    const tr = view.state.tr;

    for (const fileItem of files) {
        const id = {};
        const imageName = getGUID();
        const extension = /(?:\.([^.]+))?$/.exec(fileItem.name)[1];

        tr.setMeta(placeholderPlugin, {
            add: { id, pos: tr.selection.from }
        });
        view.dispatch(tr);

        const reader = new FileReader();
        reader.onload = () => {
            this._imageUploadClient
                .upload(`${imageName}.${extension}`, reader.result)
                .then(imageUrl => {
                    const pos = findPlaceholder(view.state, id);
                    if (pos == null) {
                        return;
                    }
                    view.dispatch(
                        tr
                            .replaceWith(
                                pos,
                                pos,
                                this._editorSchema.nodes.image.create({
                                    src: imageUrl
                                })
                            )
                            .setMeta(placeholderPlugin, { remove: { id } })
                    );
                })
                .catch(error => {
                    console.error(error);
                    view.dispatch(
                        tr.setMeta(placeholderPlugin, { remove: { id } })
                    );
                });
        };
        reader.onerror = () => {
            console.error(reader.error);
            view.dispatch(
                tr.setMeta(placeholderPlugin, { remove: { id } })
            );
        };
        reader.readAsDataURL(fileItem);

    }

    this._fileInputRef.current.value = '';
    view.focus();
};

I think the issue is with the const tr = view.state.tr; line. A transaction depends on the state of the editor. In this case you do create a transaction, the state gets modified ( by the other image which finishes uploading ), and then you try to apply an old transaction. The solution is to create a new transaction every time you call view.dispatch ( and obviously use the new transaction ).

So for ex.:

view.dispatch(
                        tr
                            .replaceWith(
                                pos,
                                pos,
                                this._editorSchema.nodes.image.create({
                                    src: imageUrl
                                })
                            )
                            .setMeta(placeholderPlugin, { remove: { id } })
                    );

should be:

const insertImageTr = view.state.tr;
view.dispatch(
                        insertImageTr 
                            .replaceWith(
                                pos,
                                pos,
                                this._editorSchema.nodes.image.create({
                                    src: imageUrl
                                })
                            )
                            .setMeta(placeholderPlugin, { remove: { id } })
                    );

That works! Thank you my friend!

1 Like