2

I would like to have a page that renders a Slate Editor, retrieves a document from an API, and then prints the window when the Editor has updated and finished re-rendering. How can I identify when the Editor has re-rendered?

Example code:

  componentDidMount() {
    $.get('/api/')
      .done((data) => {
        this.setState({ document: Value.fromJSON(data) });
        // workaround to try and make sure document is rendered
        // what can I do here instead??
        setTimeout(() => window.print(), 500);
      });
  }

  render() {
    const { document } = this.state;

    return (
        <Editor
          value={document}
          readOnly
          ...
        />
    );

I tried using componentDidUpdatein the parent component:

componentDidUpdate(prevProps, prevState) {
    window.print();
}

But the function gets triggered before the editor is fully rendered, so the text doesn't display in the Print dialog:

componentDidUpdate too early

wasabigeek
  • 2,923
  • 1
  • 21
  • 30

2 Answers2

2

Use the componentDidUpdate life-cycle method in the class that renders your Editor component.

componentDidUpdate() {
    if(this.state.document !== prevState.document) }
        // Here you know that the Editor component has re-rendered
    }
}

As noted in the documentation I linked, the componentDidUpdate method does not get triggered on initial render. This won't be an issue for your use case though because you are awaiting asynchronously loaded data.

Kyle Richardson
  • 5,567
  • 3
  • 17
  • 40
  • It works! But why does the comparison with prevState help? – wasabigeek Mar 02 '18 at 03:04
  • Ok, sorry on further refreshes it still seems to trigger `window.print` too early. – wasabigeek Mar 02 '18 at 03:10
  • Any other advice Kyle? Or is it something Slate itself needs to expose? – wasabigeek Mar 02 '18 at 11:44
  • The comparison with prevState is done so that your logic is only executed when there is an actual change in the document. As for it not working still... Let me look into the Slate editor code. I'll get back to you. – Kyle Richardson Mar 02 '18 at 19:33
  • So I just looked through the editor and slate code. The `componentDidUpdate` method in the parent that renders the editor will always be called after the completion of the editor's render method. I will need a little more information to debug your problem. Can you make a live example of your code on something like https://codesandbox.io/ so I can debug live code? – Kyle Richardson Mar 02 '18 at 19:47
  • I can't replicate the exact issue on codesandbox (for the most part it renders fine, except sometimes images aren't fully loaded before print is called), so I guess it's probably something with my project's configuration and loading of css. I'll see if I can try again later. – wasabigeek Mar 05 '18 at 08:44
1

You can use componentDidUpdate to track when state gets updated. When you update this.state.document, it looks like that will trigger a re-render of the Editor component since it is being passed down as a prop. Unless Editor provides a callback for when it renders, you will have to do the setTimeout hack:

componentDidUpdate(prevProps, prevState) {
    if(this.state.document !== prevState.document) {
        setTimeout(() => window.print(), 500);
    }
}
Chase DeAnda
  • 15,963
  • 3
  • 30
  • 41
  • Why would you use `setTimeout`? Child components render methods are completed before the `componentDidUpdate` is triggered in the parent. – Kyle Richardson Mar 01 '18 at 16:30
  • I didn't know that `componentDidUpdate` waited for ALL children to re-render? Are you sure that's the case? – Chase DeAnda Mar 01 '18 at 17:08
  • 1
    Here is a [code sandbox](https://codesandbox.io/s/rwj4xq7zxp) test you can play with. Click the button and watch the console. You can add more child components to see it continues to work the same. – Kyle Richardson Mar 01 '18 at 17:59