Why Flow cannot call ReactDOM.render with document.getElementById(…)

I was getting this error below in Flow type checking.

Cannot call ReactDOM.render with document.getElementById(...) bound to container because null [1] is
incompatible with Element [2].

     src/index.js
      26│       </Switch>
      27│     </ScrollToTop>
      28│   </BrowserRouter>
      29│ </Provider>, document.getElementById("root"));
      30│

     /private/tmp/flow/flowlib_174a8121/dom.js
 [1] 646│   getElementById(elementId: string): HTMLElement | null;

     /private/tmp/flow/flowlib_174a8121/react-dom.js
 [2]  18│     container: Element,

The code is below.
// @flow
"use strict";
import React from "react";
import ReactDOM from "react-dom";
import {createStore, applyMiddleware} from "redux";
import {Provider} from "react-redux";
import {BrowserRouter, Switch, Route} from "react-router-dom";
import Home from "./components/home";
import Detail from "./components/detail";
import LevelOfGame from "./components/level-of-game";
import NotFound from "./components/not-found";
import ScrollToTop from "./components/scroll-to-top";

import reducers from "./reducers";

const createStoreWithMiddleware = applyMiddleware()(createStore);

ReactDOM.render(<Provider store={createStoreWithMiddleware(reducers)}>
  <BrowserRouter>
    <ScrollToTop>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/detail/:detailId" component={Detail}/>
        <Route path="/level-of-game" component={LevelOfGame}/>
        <Route path="*" component={NotFound} status={404}/>
      </Switch>
    </ScrollToTop>
  </BrowserRouter>
</Provider>, document.getElementById("root"));

I believe I had to specify the type somehow in getElementById.

So I fixed the error by storing document.getElementById("root"); in a constant variable with a type specification:

const root: any = document.getElementById("root");

The error is fixed and I hope this is useful for other folks, but I’d love to understand what was causing this error. Can anyone be so kind to tell me what this was?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Aleksey L. got this first in the comments, I wanted to bring this info up to the answer level for easier visual scanning.

Flow is letting you know that the call document.getElementById("root"); can return null in which case the app would completely crash. So let’s guard against that:

const root = document.getElementById('root')

if (root !== null) {
  ReactDOM.render(<App /> , root)
}

Granted, this can feel a little annoying given that in all likelihood you will be controlling the HTML you are rendering into.

Method 2

While Cogell’s answer is correct, I would argue to keep the code simpler and add an exception.

ReactDOM.render(
 <Provider store={createStoreWithMiddleware(reducers)}>
  <BrowserRouter>
    <ScrollToTop>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/detail/:detailId" component={Detail}/>
        <Route path="/level-of-game" component={LevelOfGame}/>
        <Route path="*" component={NotFound} status={404}/>
      </Switch>
    </ScrollToTop>
  </BrowserRouter>
 </Provider>, // $FlowIgnore
 document.getElementById("root")
);

Notice the “$FlowIgnore” comment

and then in your .flowconfig file add this to the “options” field:

suppress_comment= \(.\|n\)*\$FlowIgnore


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x