TypeError: document.getElementById(...) is null

Hello all,

I’m trying to do the Markdown Previewer challenge in create-react-app and I’m having some trouble. My code seems to work when I navigate to the page after adding it and React automatically updates it, but once I manually refresh the page it breaks.

The specific line that breaks the code is:

document.getElementById("preview").innerHTML = marked(this.state.value);

If I transfer the code over to CodePen I’m still getting the same error.

From what I’ve read online, it looks like the error is there because this is loading before it sees the preview ID. The suggested solutions read to make sure I have the script tag at the bottom of the body of the document, but I can’t do these because a) Marked is imported via npm into create-react-app and there’s no script tag in the HTML document, b) CodePen is handling it on it’s own.

I doubt that’s why I’m actually getting the error, but that’s what I’m gathering from what I’ve read. Please correct me on this if you know the answer as well! :smiley:

Anyway, I’ve provided my JSX below. If anyone has any insight on how I can get this to continue working, and then I can try and finish up the challenge, it would be much appreciated. :smiley:

import marked from 'marked';
import React from 'react';
import './App.css';

class MarkdownPreviewer extends React.Component{
  constructor(props){
    super(props);
    this.state = {
      value: ""
    }
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event){
    this.setState({
      value: event.target.value
    });
  }

  render(){

    document.getElementById("preview").innerHTML = marked(this.state.value);

    return(
      <div className="container-fluid">
        <div className="row">
          <div className="col-md text-center">
            <h1>Markdown Previewer</h1>
          </div>
        </div>
        <div className="row">
          <div className="col-md">
            <h2>Enter Markdown</h2>
            <textarea id="editor" value={this.state.value} onChange={this.handleChange}/>
          </div>
          <div className="col-md">
            <h2>Preview Markdown</h2>
              <div id="preview">
              </div>
          </div>
        </div>
      </div>
    );
  }
}

class App extends React.Component{
  render(){
    return(
      <MarkdownPreviewer />
    );
  }
}


export default App;

Thanks,
Nathan

I don’t know, but a few thoughts.

  1. You may be getting this error because you are trying to access that element before it has been created, before it has been returned but the render method of the component. The first time that method is run, that element doesn’t yet exist, I would think.

  2. You shouldn’t be trying to manipulate the DOM while using React. React manages that. React keeps track of changes to the DOM so it knows when and how to update - it can’t do that if you are back channel manipulating the DOM.

I would have expected instead, something like:

              <div id="preview">
                { marked(this.state.value) } 
              </div>

Hi Kevin,

Yeah, that is how I originally wrote it, but the document.getElementById wasn’t working in there for some reason. I’ll try fooling around with it again later, but from my limited experience I had to use the document.getElementById.innerHTML otherwise it rendered the HTML as plain text and not as the actual element I was trying to render.

Update: I ended up getting it to work with dangerouslySetInnerHTML, although this is apparently not a great way to do it since in a production environment it can open the site up to XSS attacks. If anyone happens to know a better way to set this up I’m all ears.

It’s been a while since I did this, but that seems to ring a bell. I thought I heard of another option, but I wouldn’t worry about it - this is just a little project.