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

side effects of onLoad implementation #22

Open
HyperMaxime opened this issue Jun 6, 2018 · 16 comments
Open

side effects of onLoad implementation #22

HyperMaxime opened this issue Jun 6, 2018 · 16 comments

Comments

@HyperMaxime
Copy link

The passed wrapper prop onLoad function gets called in direct response (synchronous) to the onLoad callback of the script being loaded. This does not always guarantee that the underlying component is already loaded by then.

For example the editor can be in specific route of a react application, and load the unlayer script the first time the user gets there. If the user then navigates away from that route and comes back, the script will be loaded from the cache, therefore immediately kicking in the onLoad callback.
In the demo, the onLoad function will execute this.editor.loadDesign(sample) and in my particular case add event listeners. It then blows up saying this.editor is undefined.

@umairsiddique
Copy link
Contributor

@HyperMaxime can you share a functional code sample of your particular scenario? If not, then any snippets would help too.

@stroypet
Copy link

@umairsiddique I also get the same issue from time to time.

My onLoad function:

  onLoad = () => {
    this.editor.loadDesign(this.props.firm_prefs[1].email_templates[this.state.selectedTemplate]);
    this.editor.setMergeTags([ {name: "First Name", value: "{*|first_name|*}"}, {name: "Last Name", value: "{*|last_name|*}"}, {name: "Invite Link", value: "{*|invite_link|*}"}]);
  }

Where I use the onLoad:

              <EmailEditor
              onLoad={this.onLoad} 
              ref={editor => this.editor = editor}
              />
            </div>

Error it spits out:

image

@HyperMaxime
Copy link
Author

@umairsiddique I created a CodeSandbox that illustrates the issue: https://codesandbox.io/s/5kz6nzjq9p

By showing and hiding the editor, you'll see in the log that the editor is undefined when the onLoad callback function executes.

Also notice that sometimes it is actually available at the start, depending on the browser having cached the unlayer script.

@stroypet
Copy link

stroypet commented Jun 18, 2018

@umairsiddique @HyperMaxime sorry to be a pest, but did either of you ever find a solution or work around to this issue?

Cheers.

@coxom
Copy link

coxom commented Jun 18, 2018

@stroypet until we get a better solution/fix we are just pointing to the global variable unlayer.

@stroypet
Copy link

@coxom Thanks for the reply, don't suppose I could bother you for a code snippet of what that looks like? My .js skills aren't the greatest :).

@HyperMaxime
Copy link
Author

@stroypet The usage example would become something like this:

import React, { Component } from 'react'
import { render } from 'react-dom'
import EmailEditor from 'react-email-editor'

class App extends Component {
  render() {
    return <div>
      <h1>react-email-editor Demo</h1>
      <div>
        <button onClick={this.exportHtml}>Export HTML</button>
      </div>
      <EmailEditor />
    </div>
  }

  exportHtml = () => {
    unlayer.exportHtml(data => {
      const { design, html } = data
      console.log('exportHtml', html)
    })
  }
}

render(<App />, document.getElementById('app'))

@car1sberg
Copy link

Solution with the global variable works. Just use window.unlayer. Was trying to fix it half a day, thank you all. It is not good to use global variables in React but who cares, IT WORKS! ))

@nathanhfoster
Copy link

nathanhfoster commented Oct 7, 2018

The key fix is to check if this.editor && window.unlayer != undefined and by conditionally setting the onLoad prop. In my particular use case, I am using react-router-dom and an api call that Redux dispatches when the componentDidMount. I figured out this fix (fyi this.props.match is a prop passed in from react-router-dom so ignore it if your component is not using it):

// Function outside render but inside class MyClassName extends Component { }
 loadDesign = design => this.editor.loadDesign(design)

 render() {
// Check if Redux updated the state from mapStateToProps
const design = this.state.HtmlDocument.hasOwnProperty('design') ? JSON.parse(this.state.HtmlDocument.design) : null

// True if there are paramaters in the url, redux updated the state, and if the editor has loaded into memory
 const isEditingDesign = this.props.match && design && this.editor && window.unlayer

return (
<EmailEditor minHeight="85vh" ref={editor => this.editor = editor} style={styles} onLoad={isEditingDesign ? this.loadDesign(design) : null}/>
)
}
export default withRouter(reduxConnect(mapStateToProps, mapDispatchToProps)(MyClassName))

If anyone needs additional clarity, please respond to this comment. I am willing to share more code.

@HyperMaxime
Copy link
Author

@strap8, I did not test your code in practise so I might be wrong, but at first sight I think your solution misses the point of this issue. Under certain conditions, this.editor will not yet be available when the onLoad callback gets called, therefore throwing an exception if any method of it is being executed. Your solution simply doesn't run any callback in that scenario, which will indeed not throw any exception, but will also eventually not run code that is expected to run at the moment the editor is loaded.

@mvcarvalho
Copy link

I'm using a workaround to fix this. I facing this problem when the route change, using react-router-dom.

So, to "solve" this, I'm using 2 flags: isEditorLoaded and isComponentMounted

Initiate them in the constructor:
constructor(props) { super(props); this.editor = null; this.isEditorLoaded = false; this.isComponentMounted = false; }

Implement the hook:
componentDidMount() { this.isComponentMounted = true; this.loadTemplate(); }

Implement the onLoad:
onLoad = () => { this.isEditorLoaded = true; this.loadTemplate(); }

Then a function to be called and check if we are able to use the editor:
loadTemplate = () => { if (!this.isEditorLoaded || !this.isComponentMounted) return; this.editor.loadDesign(this.state.jsonTemplate) }

The render:
<EmailEditor ref={editor => this.editor = editor} onLoad={this.onLoad} />

I hope this can help.

@andrewstudionone
Copy link

This solution works for me #7 (comment)

@ZeroDarkThirty
Copy link

ZeroDarkThirty commented Dec 11, 2019

@mvcarvalho This worked for me when trying to use setMergeTags. It feels like the best workaround so far. Have you managed to get it working in a different way since your post?

@Crimiro
Copy link

Crimiro commented Jun 9, 2020

@mvcarvalho Works for me, thank you!

@andrewlorenz
Copy link

in case it helps anyone, I have posted up a working solution at #100 (comment)

@igoswamik
Copy link

Sometimes the email editor shows up but most of the time on refreshing/reconnecting I get these errors in the browser console. The same thing happens in the case of "save Design" and "load design" both

  1. ERROR: (Cannot read property save Design/ load Design of Undefined)
  2. ERROR: (Prop id did not match. Server: "editor-2" Client: "editor-1")

sometimes even with ERROR2 things work fine.
WHAT SHOULD I DO......?
image

image

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