是否可以在不使用 JSX 的情况下使用 React Hooks API(在打字稿中)

Posted

技术标签:

【中文标题】是否可以在不使用 JSX 的情况下使用 React Hooks API(在打字稿中)【英文标题】:Is it possible to use React Hooks API without using JSX (in typescript) 【发布时间】:2020-09-03 19:28:42 【问题描述】:

在将 JSX 版本 React Hooks API demo 翻译成没有 JSX 的版本时,在 react-without-jsx 之后,我得到了以下代码

import React,  useState  from 'react';
import ReactDOM from 'react-dom';

export function test() 
  ReactDOM.render(Count(), document.getElementById('main'));


export function Count() 
  const e = React.createElement;
  const [count, setCount] = useState(0);
  const button = e('button', 
    onClick: () => 
      setCount(count + 1);
    ,
  );
  return e('div', null, e('p', `You clicked $count times`), button);

但是,在浏览器中运行此代码时,出现以下错误

react.development.js:1551 Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
    at resolveDispatcher (react.development.js:1551)
    at Object.useState (react.development.js:1582)
    at Count (Count.ts:10)
    at Object.test (Count.ts:5)
    at main (index.ts:4)
    at Object../src/index.ts (index.ts:6)
    at __webpack_require__ (bootstrap:19)
    at bootstrap:83
    at bootstrap:83
resolveDispatcher @ react.development.js:1551
useState @ react.development.js:1582
Count @ Count.ts:9
test @ Count.ts:4
main @ index.ts:3
./src/index.ts @ index.ts:6
__webpack_require__ @ bootstrap:18

如果使用 JSX,上面的例子可以正常工作。

函数组件的所有示例似乎都是基于 JSX。 那么是否可以在没有 JSX 的情况下使用 React Hook?如果可以,我们该怎么做?


编辑更新: 上面的代码仍然有错误(如下面的编辑所述)。

e('p', `You clicked $count times`)

应该是

e('p', null, `You clicked $count times`)

createElement 的第二个参数应该是 props。


按照@qxg 的说明进行编辑。

在渲染过程中,我没有使用React.createElement,将代码修改为

import React,  useState  from 'react';
import ReactDOM from 'react-dom';

export function test() 
  ReactDOM.render(React.createElement(Count), document.getElementById('main'));


// ... Same code for Count as above

函数组件外调用Hooks API错误已解决!

但是,出现了新的错误,例如:

Warning: Invalid attribute name: `0`    react-dom.development.js:82
    in p (created by Count)
    in div (created by Count)
    in Count
Warning: Invalid attribute name: `1`    react-dom.development.js:82
    in p (created by Count)
    in div (created by Count)
    in Count

很多类似上面的错误,以

结尾
Warning: Invalid attribute name: `18`   react-dom.development.js:82 
    in p (created by Count)
    in div (created by Count)
    in Count

页面被渲染为

<div>
  <p></p>
  <button></button>
</div>

因此只有空元素,没有文本。 对这个问题有什么建议吗?


附言我不确定是否有 typescript/webpack 错误的配置,所以我在这里发布。

ts.config.json


  "compilerOptions": 
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    "lib": [
      "DOM",
      "DOM.Iterable",
      "ESNext"
    ] /* Specify library files to be included in the compilation. */,
    "jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
    "sourceMap": true /* Generates corresponding '.map' file. */,
    "outDir": "./dist" /* Redirect output structure to the directory. */,
    "strict": true /* Enable all strict type-checking options. */,
    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
    "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,

    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,

    "skipLibCheck": true /* Skip type checking of declaration files. */,
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  

webpack.config.js

const path = require('path');

module.exports = 
  mode: 'development',
  entry: './src/index.ts',
  // Enable sourcemaps for debugging webpack's output.
  devtool: 'source-map',

  resolve: 
    // Add '.ts' and '.tsx' as resolvable extensions.
    extensions: ['.ts', '.tsx'],
  ,

  module: 
    rules: [
      
        test: /\.ts(x?)$/,
        exclude: /node_modules/,
        use: [
          
            loader: 'ts-loader',
          ,
        ],
      ,
      // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
      
        enforce: 'pre',
        test: /\.js$/,
        loader: 'source-map-loader',
      ,
    ],
  ,
  externals: 
    react: 'React',
    'react-dom': 'ReactDOM',
  ,
  output: 
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist'),
  ,
;

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello React!</title>
  </head>
  <body>
    <div id="main"></div>
    <script src="./node_modules/react/umd/react.development.js"></script>
    <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
    <!-- Main -->
    <script src="./dist/bundle.js"></script>
  </body>
</html>

【问题讨论】:

可以删除导入import ReactDOM from 'react-dom';吗?这行得通吗? 你应该打电话给ReactDOM.render(React.createElement(Count, ...吗? @John 我无法删除ReactDOM的导入,我需要使用ReactDOM.render(...) 【参考方案1】:

您手动将 jsx 转换为 React.creatElement,但也忘记转换***元素。

ReactDOM.render(React.createElement(Count), document.getElementById('main'));

【讨论】:

谢谢!这适用于解决在函数组件之外调用 Hooks API 错误。不幸的是,出现了新错误,我已经更新了问题以描述新问题。 我错了,当创建

时,我的论点是错误的。除此之外,功能组件的问题完全按照您的建议解决。谢谢。

【参考方案2】:

这是我的完整工作示例

<div id='main'></div>
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script>
const e = React.createElement;
function Count() 
  const [count, setCount] = React.useState(0);
  const button = e('button', onClick: ()=>setCount(count+1), 'Click!');
  return e('div', null, e('p', null, `You clicked $count times`), button);

ReactDOM.render(e(Count), document.getElementById('main'));
</script>

【讨论】:

以上是关于是否可以在不使用 JSX 的情况下使用 React Hooks API(在打字稿中)的主要内容,如果未能解决你的问题,请参考以下文章

React之JSX语法

我可以在不重新加载浏览器的情况下编辑 React 组件吗?

请问怎么学习react.js

是否可以在不渲染 HTML 的情况下使用 React?

如何为状态数组中的每个项目添加 React JSX?

React17 使用 JSX 的情况下无须再显式导入 React