React.js 常见问题

2025-05-27

React.js 常见问题

jsComplete,我们管理一个Slack 账户,专门帮助代码学习者摆脱困境。我们偶尔会收到一些有趣的问题,但大多数问题都是常见问题。我创建这个资源是为了针对 React.js 初学者经常遇到的常见问题编写详细的说明,这样我就可以在这里参考,而不用一遍又一遍地重复输入。


1 — 组件名称未以大写字母开头

React 组件必须有一个以大写字母开头的名称。

如果组件名称不以大写字母开头,则组件使用将被视为内置元素,例如divspan

例如:

class greeting extends React.Component { 
  // ...
}
Enter fullscreen mode Exit fullscreen mode

如果您尝试渲染<greeting />,React 将忽略上述内容,并且您将收到警告:

Warning: The tag <greeting> is unrecognized in this browser. 
If you meant to render a React component, 
start its name with an uppercase letter.
Enter fullscreen mode Exit fullscreen mode

这里更大的问题是当你决定命名你的组件button或时img。React 将忽略你的组件并仅渲染原始 HTMLbuttonimg标签。

请注意,上面的“My Awesome Button”并没有渲染出来,React 只是渲染了一个空的 HTML 按钮元素。在这种情况下,React 不会发出警告。

2 — 使用单引号代替反引号

用反引号 (`...`) 创建的字符串与用单引号 ('...') 创建的字符串不同。

tab在大多数键盘上,可以使用键上方的键输入反引号 (`) 字符

当我们需要在字符串中包含动态表达式(而不诉诸字符串连接)时,我们使用反引号创建一个字符串。

`This is a string template literal that can include expressions`

'This is just a string, you cannot include expressions here'
Enter fullscreen mode Exit fullscreen mode

假设您想要一个始终报告当前时间的字符串: 

“Time is ...”

// Current time string
const time = new Date().toLocaleTimeString();

// When using regular strings (single or double quotes),
// you need to use string concatenation:
'Time is ' + time

// When using back-ticks,
// you can inject the time in the string using ${}
`Time is ${time}`
Enter fullscreen mode Exit fullscreen mode

此外,当使用字符串文字(带有反引号)时,您可以创建跨越多行的字符串:

const template = `I

CAN

SPAN

Multiple Lines`;
Enter fullscreen mode Exit fullscreen mode

您不能使用常规字符串来做到这一点。


3—使用 React.PropTypes

PropTypes对象已从 React 中移除。它之前可用,React.PropTypes但现在无法再使用。

相反,您需要:

  1. 将新的prop-types包添加到您的项目中:npm install prop-types
  2. 导入:import PropTypes from 'prop-types'

然后就可以使用了。例如:PropTypes.string

如果您错误地使用React.PropTypes,您将收到如下错误:

TypeError: Cannot read property 'string' of undefined
Enter fullscreen mode Exit fullscreen mode

4 — 没有使用教程中提到的正确版本

在观看或阅读有关编码的内容并遵循其中提供的示例时,请确保使用内容中所使用的工具的正确版本。通常,使用每个工具的最新版本是安全的,但如果内容有点旧,您可能会遇到一些弃用问题。

为了安全起见,请坚持使用所用工具的主要版本。例如,如果本教程使用的是 React 16,请不要继续使用 React 15。

这对于 Node.js 来说尤其重要。如果您使用旧版本的 Node,将会面临重大问题。例如,如果您正在学习某个教程,Object.values并且使用的是 Node 6.x,那么该方法在当时并不存在。您需要 Node 7.x 或更高版本。


5——函数与类的混淆

你能说出下面的代码有什么问题吗?

class Numbers extends React.Component { 
  const arrayOfNumbers = _.range(1, 10);
  // ...
}
Enter fullscreen mode Exit fullscreen mode

上面的代码是无效的,因为在 JavaScript 类的主体内,你几乎无法执行任何操作。你只能使用有限的语法来定义方法和属性。

这有点令人困惑,因为 {} 类语法中使用的看起来像普通的块范围,但事实并非如此。

在基于函数的组件中,您可以自由地做任何事情:

// Totally Okay:

const Number = (props) => { 
  const arrayOfNumbers = _.range(1, 10);
  // ...
};
Enter fullscreen mode Exit fullscreen mode

6 — 将数字作为字符串传递

您可以使用字符串传递 prop 值:

<Greeting name="World" />
Enter fullscreen mode Exit fullscreen mode

如果需要传递数值,请不要使用字符串:

// Don't do this
<Greeting counter="7" />
Enter fullscreen mode Exit fullscreen mode

相反,使用花括号传递实际的数值:

// Do this instead
<Greeting counter={7} />
Enter fullscreen mode Exit fullscreen mode

{7}在组件内部使用Greetingthis.props.counter将获得实际的数值7,并且可以安全地对其进行数学运算。如果您将其作为参数传递“7”,然后将其视为数字,则可能会遇到意外的结果。


7 — 忘记另一个应用程序实例仍在使用同一端口

要运行 Web 服务器,您需要使用主机(如 127.0.0.1)和端口(如 8080)来使服务器监听有效的 http 地址上的请求。

一旦 Web 服务器成功运行,它就拥有了该端口的控制权。您不能将同一端口用于其他用途。该端口将处于繁忙状态。

如果你尝试在另一个终端运行同一个服务器,你会收到一个错误,提示端口“正在使用”。例如:

Error: listen EADDRINUSE 127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

请注意,有时 Web 服务器可能正在后台运行,或者在分离的 screen/tmux 会话中运行。您看不到它,但它仍然占用着端口。要重启服务器,您需要“终止”仍在运行的服务器。

要识别使用特定端口的进程,您可以使用类似命令ps(以及grep有关您的应用程序的某些内容),或者如果您知道端口号,则可以使用以下lsof命令:

lsof -i :8080
Enter fullscreen mode Exit fullscreen mode

8 — 忘记创建环境变量

有些项目依赖于 shell 环境变量的存在才能启动。如果您在缺少所需环境变量的情况下运行这些项目,它们将尝试使用未定义的值,并可能引发一些难以理解的错误。

例如,如果一个项目连接到 MongoDB 之类的数据库,它很可能使用类似的环境变量process.env.MONGO_URI来连接它。这使得该项目可以在不同的环境中使用不同的 MongoDB 实例。

要在本地运行连接到 MongoDB 的项目,您必须MONGO_URI先导出环境变量。例如,如果您在端口 上运行本地 MongoDB 27017,则需要在运行项目之前执行此操作:

export MONGO_URI="mongodb://localhost:27017/mydb"
Enter fullscreen mode Exit fullscreen mode

您可以grep项目源代码来process.env找出它需要哪些环境变量才能正常工作。

9——混淆花括号 {} 和圆括号 ()

而不是:

return { 
  something();
};
Enter fullscreen mode Exit fullscreen mode

你需要:

return ( 
  something();
);
Enter fullscreen mode Exit fullscreen mode

第一个将尝试(并失败)返回一个对象,而第二个将正确调用该something()函数并返回该函数返回的内容。

由于<tag>JSX 中的任何内容都会转换为函数调用,因此在返回任何 JSX 时都会出现此问题。

这个问题在箭头函数的短语法中也很常见。

而不是:

const Greeting = () => { 
  <div> 
    Hello World 
  </div>
};
Enter fullscreen mode Exit fullscreen mode

你需要:

const Greeting = () => ( 
  <div> 
    Hello World 
  </div>
);
Enter fullscreen mode Exit fullscreen mode

当你在箭头函数中使用花括号时,就开启了该函数的作用域。箭头函数的简短语法不使用花括号。


10 — 不使用括号包裹对象

当您想要创建一个返回普通对象的短箭头函数时,上面的花括号与圆括号的问题也会令人困惑。

而不是:

const myAction = () => { type: 'DO_THIS' };
Enter fullscreen mode Exit fullscreen mode

你需要:

const myAction = () => ({ type: 'DO_THIS'});
Enter fullscreen mode Exit fullscreen mode

如果不把对象括在括号里,你就不能使用短语法。你实际上是在为一个字符串定义一个标签!

这在方法的更新函数中很常见setState,因为它需要返回一个对象。如果要使用短箭头函数语法,则需要用括号将该对象括起来。

而不是:

this.setState(prevState => { answer: 42 });
Enter fullscreen mode Exit fullscreen mode

你需要:

this.setState(prevState => ({ answer: 42 }));
Enter fullscreen mode Exit fullscreen mode

11 — API 元素和属性的大小写不正确

不是React.Component,不是React.component。不是componentDidMount,不是ComponentDidMount通常 ReactDOM不是ReactDom

请注意您需要的 API 大小写。如果使用不正确的大小写,您收到的错误可能无法清楚地说明问题所在。

react和导入时react-dom,请确保导入的名称正确,并且所使用的内容与导入的内容完全相同。ESLint 可以帮助您指出未使用的内容。

在访问组件 props 时,这个问题也很常见:

<Greeting userName="Max" />

// Inside the component, you need 
props.userName
Enter fullscreen mode Exit fullscreen mode

如果props.userName您错误地使用了props.username或 而不是props.UserName,那么您将使用未定义的值。请注意这一点,或者更好的是,让您的 ESLint 配置也指出这些问题。


12. 混淆状态对象和实例属性

在类组件中,您可以定义本地state对象,然后使用以下命令访问它this

class Greeting extends React.Component { 
  state = { 
    name: "World", 
  };

  render() { 
    return `Hello ${this.state.name}`;
  }
}
Enter fullscreen mode Exit fullscreen mode

以上将输出“Hello World”。

您还可以除了状态之外定义其他本地即时属性:

class Greeting extends React.Component { 
  user = { 
    name: "World", 
  };

  render() { 
    return `Hello ${this.user.name}`;
  }
}
Enter fullscreen mode Exit fullscreen mode

以上也会输出“Hello World”。

实例state属性比较特殊,因为 React 会管理它。你只能通过修改它来更改setState,React 会在你修改时做出反应。但是,你定义的所有其他实例属性都不会影响渲染算法。你可以随意更改this.user上面示例中的实例属性,React 不会触发渲染循环。


13 — 将 <tag/> 与 </tag> 混淆

不要把/结束标签中的字符放错位置。诚然,有时你可以使用<tag/>,有时则需要</tag>

HTML 中有一种叫做“自闭合标签”(又称空标签)的东西。这些标签表示没有任何子节点的元素。例如,这个img标签就是一个自闭合标签:

<img src="..." />

// You don't use <img></img>
Enter fullscreen mode Exit fullscreen mode

标签div可以有子标签,因此您可以使用开始和结束标签:

<div> 
  Children here...
</div>
Enter fullscreen mode Exit fullscreen mode

这同样适用于 React 组件。如果组件有子内容,它可能看起来像这样:

<Greeting>Hello!</Greeting>

// Notice the position of the / character.
Enter fullscreen mode Exit fullscreen mode

如果组件没有子组件,则可以使用打开/关闭标签或仅使用自关闭标签来编写它:

// 2 valid ways

<Greeting></Greeting>

<Greeting />

// Notice how the / character moves based on whether the element 
// is self-closing or not
Enter fullscreen mode Exit fullscreen mode

以下使用无效:

// Wrong

<Greeting><Greeting />
Enter fullscreen mode Exit fullscreen mode

如果你放错了/字符,你将会得到如下错误:

Syntax error: Unterminated JSX contents
Enter fullscreen mode Exit fullscreen mode

14 — 假设导入/导出功能正常

导入/导出功能是 JavaScript 的官方特性(自 2015 年起)。然而,它是唯一一个尚未在现代浏览器和最新 Node.js 中完全支持的 ES2015 特性。

React 项目的常用配置是 Webpack 和 Babel。两者都支持此功能,并将其编译为所有浏览器都能理解的内容。只有当你的流程中包含 Webpack 或 Babel 之类的工具时,才能使用 import/export。

然而,在 React 捆绑应用中启用 import/export 并不意味着你可以在任何你想要的地方使用它们!例如,如果你还通过最新的 Node 进行服务器端渲染,那么一切就无法正常工作。你很可能会收到“ unexpected token ”错误。

为了让 Node 也能理解 import/export(如果你在前端使用 import/export 并且想实现服务端渲染,这是必须的),你需要在 Node 上运行一个 Babel 预设(例如env预设),以便能够转译它们。你可以在开发环境中使用pm2nodemonbabel-watch等工具来实现这一点,并在每次更改内容时重启 Node。


15 — 不绑定处理程序方法

我把这个问题留到最后讨论,因为这是一个大问题,而且是一个非常常见的问题。

你可以在 React 组件中定义类方法,然后在组件的render方法中使用它们。例如:

class Greeting extends React.Component { 
  whoIsThis() { 
    console.dir(this); // "this" is the caller of whoIsThis 
    return "World"; 
  }

  render() { 
    return `Hello ${this.whoIsThis()}`; 
  }
}

ReactDOM.render(<Greeting />, mountNode);
Enter fullscreen mode Exit fullscreen mode

我在方法whoIsThis中使用了方法,因为在方法内部关键字指的是与代表组件的 DOM 元素关联的组件实例。renderthis.whoIsThisrenderthis

React 内部确保其类方法中的 “ ” 指向实例。但是,当你使用对该方法的引用this时,JavaScript 不会自动绑定实例whoIsThis

console.dir中的代码行将whoIsThis正确报告组件实例,因为该方法是直接render带有显式调用者( )的方法中调用的。执行上述代码时,您应该在控制台中this看到该对象:Greeting

但是,当您在延迟执行通道(例如事件处理程序)中使用相同方法时,调用者将不再显式,并且该console.dir行将不会报告组件实例。

请参阅下面的代码和输出(单击后)。

在上面的代码中,whoIsThis当你点击字符串时,React 会调用该方法,但它不会允许你访问其中的组件实例。这就是为什么我们点击字符串时会得到 的原因。如果你的类方法需要访问诸如和 之undefined类的内容,那么就会出现问题。它根本无法工作。this.propsthis.state

这个问题有很多解决方案。你可以将方法包装成内联函数,或者使用 .bindcall 强制方法记住其调用者。对于不经常更新的组件,这两种方法都可以。你还可以优化 bind 方法,将其放在类的构造函数中而不是 render 方法中执行。然而,解决这个问题的最佳方案是通过 Babel 启用 ECMAScript 类字段功能(目前仍处于 stage-3),并使用箭头函数作为处理程序:

class Greeting extends React.Component { 
  whoIsThis = () => { 
    console.dir(this); 
  }

  render() { 
    return ( 
      <div onClick={this.whoIsThis}> 
        Hello World 
      </div> 
    ); 
  }
}
Enter fullscreen mode Exit fullscreen mode

这将按预期工作:

就到这里吧。感谢阅读。


看看我的《通过构建游戏来学习 React.js》这本书:

我还有一些其他您可能感兴趣的书:

本文最初发表于此处

文章来源:https://dev.to/pluralsight/reactjs-frequently-facedproblems--l5g
PREV
您离不开的开发者工具
NEXT
我如何让我的网站在 1 秒内加载