为啥 Flow 不能用 document.getElementById(...) 调用 ReactDOM.render

Posted

技术标签:

【中文标题】为啥 Flow 不能用 document.getElementById(...) 调用 ReactDOM.render【英文标题】:Why Flow cannot call ReactDOM.render with document.getElementById(...)为什么 Flow 不能用 document.getElementById(...) 调用 ReactDOM.render 【发布时间】:2018-11-08 19:08:40 【问题描述】:

我在下面的流类型检查中收到此错误。

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,

代码如下。

// @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"));

我相信我必须以某种方式在 getElementById 中指定类型。

所以我通过将document.getElementById("root"); 存储在具有类型规范的常量变量中来修复错误:

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

错误已修复,我希望这对其他人有用,但我很想了解导致此错误的原因。谁能告诉我这是什么?

【问题讨论】:

正如错误所说,document.getElementById 可以返回 nullReactDOM.render 输入不允许。因此,要修复错误,您只需验证 root 是否具有值:const root = document.getElementById("root"); if (root) ReactDOM.render(... 【参考方案1】:

Aleksey L. 在 cmets 中首先获得此信息,我想将此信息提升到答案级别,以便更轻松地进行视觉扫描。

Flow 让您知道调用 document.getElementById("root"); 可以返回 null,在这种情况下应用程序将完全崩溃。所以让我们提防这种情况:

const root = document.getElementById('root')

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

当然,这可能会让人觉得有点烦人,因为您很可能会控制要渲染到的 HTML。

【讨论】:

【参考方案2】:

虽然 Cogell 的回答是正确的,但我主张保持代码更简单并添加异常。

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")
);

注意“$FlowIgnore”注释

然后在您的.flowconfig 文件中将其添加到“选项”字段中:

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

【讨论】:

以上是关于为啥 Flow 不能用 document.getElementById(...) 调用 ReactDOM.render的主要内容,如果未能解决你的问题,请参考以下文章

用原生Dom实现向表格中添加数据

js原生添加事件的方式

JS获取内联样式

JavaScript获取页面元素

Firefox JavaScript填写表单

在Codemirror上显示CoffeeScript Lint