Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uncaught at handleCall TypeError: Cannot set property 'onload' of null #78

Open
andrewhl opened this issue Sep 9, 2017 · 5 comments
Open

Comments

@andrewhl
Copy link

andrewhl commented Sep 9, 2017

I'm using react-tinymce with create-react-app.

I'm getting the following error when the editor component mounts:

uncaught at handleCall TypeError: Cannot set property 'onload' of null
    at p.unbindAllNativeEvents (https://cloud.tinymce.com/stable/tinymce.min.js?apiKey=s057mcau7lzqdzu5tu3vx99qiek91pkj0od7u00dbw6kuk65:18:2293)
    at p.remove (https://cloud.tinymce.com/stable/tinymce.min.js?apiKey=s057mcau7lzqdzu5tu3vx99qiek91pkj0od7u00dbw6kuk65:20:9142)
    at Object.execCommand (https://cloud.tinymce.com/stable/tinymce.min.js?apiKey=s057mcau7lzqdzu5tu3vx99qiek91pkj0od7u00dbw6kuk65:20:19531)
    at Object._remove (http://localhost:3000/static/js/bundle.js:127305:27)
    at Object.componentWillUnmount (http://localhost:3000/static/js/bundle.js:127245:10)

The section in question is in tinymce.js:

            unbindAllNativeEvents: function() {
                var a, b = this;
                if (b.delegates) {
                    for (a in b.delegates)
                        b.dom.unbind(d(b, a), a, b.delegates[a]);
                    delete b.delegates
                }
                b.inline || (b.getBody().onload = null,
                b.dom.unbind(b.getWin()),
                b.dom.unbind(b.getDoc())),
                b.dom.unbind(b.getBody()),
                b.dom.unbind(b.getContainer())
            }

b.getBody() is returning null. This only happens intermittently. Sometimes the editor loads correctly. I should note that I am integrating the editor into react-redux-form, as a custom Control component.

    render(): React.Element<*> {
        return (
            <div>
                <input type="file" id="image-upload-tinymce" name="single-image" style={{ display: "none" }}
                       accept="image/png, image/gif, image/jpeg, image/jpg, image/svg" />
                <UIForm
                    as={Form}
                    model={this.props.formModel}
                    onSubmit={(foo) => this.handleSubmit(foo)}
                >
                    <Control
                        model={this.props.fieldModel}
                        component={TinyMCECustom}
                        mapProps={{
                            content: (props) => props.viewValue,
                        }}
                        updateContent={this.props.updateContent}
                        validators={{
                            required: val => val && val.length > 10
                        }}
                    />
                </UIForm>
            </div>
        );
    }

I am initializing the value of the form from a reducer that react-redux-form connects to my redux state. The reducer has the following structure:

export default function reducer(state = initialState, action) {
    switch (action.type) {
    case articleModule.GET_SUCCESS:
    case articleModule.SAVE_SUCCESS: {
        const article = action.payload.data;
        return {
            ...state,
            description: article.description || '',
            uuid: article.uuid,
        }
    }
    default:
        return state;
    }
}

Sometimes the editor loads just fine, prepopulating with the description of article from the reducer. This suggests to me that the issue is asynchronous, and is happening when the TinyMCE component attempts to mount before it has received the data from the react-redux-form reducer. I am setting default values in the reducer, however, so I'm not sure if something else is causing this issue.

This is my implementation of TinyMCECustom:

const filePickerCallback = (callback, value, meta) => {
    if (meta.filetype !== 'image') {
        return;
    }

    let input = document.getElementById('image-upload-tinymce');
    input.click();

    input.onchange = () => {
        let file = input.files[0];
        let reader = new FileReader();

        reader.onload = (e) => {
            let img = new Image();
            img.src = reader.result;

            callback(e.target.result, {
                alt: file.name
            });
        };

        reader.readAsDataURL(file);
    };
}

const handleEditorChange = (e, props) => {
    props.updateContent(e.target.getContent());
};

const handleOnBlur = (e, props) => {
    props.updateContent(e.target.getContent());
}

const handleOnKeyup = (e, props) => {
    const updateContent = _.debounce(() => props.updateContent(e.target.innerHTML), 300);
    updateContent();
}

const TinyMCECustom = (props) => {
    return <TinyMCE
        content={props.content}
        config={{
            plugins: ['advlist autolink lists link image charmap print preview hr anchor pagebreak',
                'searchreplace wordcount visualblocks visualchars code fullscreen',
                'insertdatetime media nonbreaking save table contextmenu directionality',
                'emoticons template paste textcolor colorpicker textpattern imagetools codesample toc help'],
            toolbar1: 'undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
            toolbar2: 'print preview media | forecolor backcolor emoticons | codesample help',
            image_advtab: true,
            file_browser_callback_types: 'image',
            file_picker_callback: filePickerCallback,
            branding: false,
            height: 400,
        }}
        {...props}
        onChange={(e) => handleEditorChange(e, props)}
        onBlur={(e) => handleOnBlur(e, props)}
        onKeyup={(e) => handleOnKeyup(e, props)}
    />
}
@codejunkyard
Copy link

codejunkyard commented Oct 23, 2017

I have a similar issue working with react-tinymce (0.6.0) and redux-form (7.1.1) while loading my data asynchronously. @andrewhl, have you found a work-around in the mean-time?

Just upgraded to react-tinymce 0.7.0 and I get the same error.

@andrewhl
Copy link
Author

@gvautour Unfortunately, I have not. The only solution that seems to accomplish anything is setting a manual setTimeout of 2-3 seconds before rendering the editor. This is, obviously, not ideal. Please let me know if you find anything. I'll post this to Stack Overflow and see if I get any hits.

@andrewhl
Copy link
Author

@gvautour Here's my StackOverflow post

@andrewhl
Copy link
Author

andrewhl commented Nov 5, 2017

@gvautour I found a viable workaround. The one I posted above doesn't actually work. Upgrade to React 16, and use an error boundary component. See this article.

You wrap your TinyMCE component in <PotentialError></PotentialError>, and React will re-render the component if it throws the onload error. It'll still error in the console, but the wysiwyg editor will rerender and be usable.

@MarkyMarkMcDonald
Copy link

For others stumbling across this: It looks like this is fixed in version 4.7.7 of TinyMCE:
https://github.com/tinymce/tinymce/blob/6b0075fdac4d190614de7d815d067b93300f029d/changelog.txt#L140

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants