在 jsComplete,咱们管理一个专门用于帮助编程学习者 slack 账户。咱们经常会收到一些有趣的问题,但大多数问题都是常见问题。 我建立这个资源为了帮助 React.js学习者遇到这些常见的问题时提供必定帮助。在这里能够快速找到一些常见问题的解决方案,而不是一,遍又一遍去找解决方法,我会持续更新这些常见的问题。前端
想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你!node
React 组件名称必须具备以大写字母开头。react
若是组件名称不以大写字母开头,则组件使用将被视为内置元素,例如 div
或 span
。git
例如:github
class greeting extends React.Component { // ... }
若是尝试渲染 <greeting />
,React 将忽略上述内容,会报如下警告:web
Warning: The tag <greeting> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
这里更大的问题是把组件命名为 button
或 img
。 React 会忽略你的组件,只是渲染一个普通 HTML button
或 img
标签。算法
注意上面没有渲染 “My Awesome Button” 和 React 刚刚呈现的空 HTML 按钮元素。 在这种状况下,React 不会警告你。mongodb
用反引号(`…`)
建立的字符串与用单引号('…')
建立的字符串不一样。shell
在大多数键盘上,可使用 tab 键上方的键来输入用反引号( ` )字符。数据库
当须要在字符串中包含动态表达式时,使用反引号建立一个字符串(不须要使用字符串链接)。
`This is a string template literal that can include expressions` 'This is just a string, you cannot include expressions here'
假设你想要一个始终报告当前时间的字符串:
“Time is ...”
// Current time string const time = new Date().toLocaleTimeString(); // 使用普通字符串(单引号或双引号)时,须要使用字符串链接: 'Time is ' + time // 在使用反引号时,可使用 ${} 在字符串中注入时间 `Time is ${time}`
此外,反引号还声明一个字符串时,能够建立一个跨多行的字符串:
const template = `I CAN SPAN Multiple Lines`;
常规字符串不能这样作。
PropTypes
对象已从 React 中删除。 其过去是以 React.PropTypes 的形式被使用,但不能再使用它了。
相应的,你须要:
npm install prop-types
import PropTypes from 'prop-types'
而后就可使用它啦,如: PropTypes.string
。
若是你错误地使用了 React.PropTypes
,会获得这样的错误提示:
TypeError: Cannot read property 'string' of undefined
在看或阅读有关代码的内容以及使用指南里的例子时,确保你使用的库版本与例子的里的版本是一致的。通常使用最新版本是没有问题,可是若是内容过期,你可能会遇到一些弃用问题。
为了安全起见,请使用主干版本。 例如,若是教程里使用的是 React 16,本身就不要使用 React 15 进行开发了。
这对 Node.js 也很重要。若是使用旧版本的 Node,会遇到一系列问题。 例如,若是你正在看一些教程,这些教程使用了 Object.values
,而你如今用 Node 6.x,那个版本此方法是不存在的。 你须要 Node 7.x 或更高版本。
你能看出下面的代码有什么问题吗?
class Numbers extends React.Component { const arrayOfNumbers = _.range(1, 10); // ... }
上面的代码是无效的,由于在 JavaScript 类的内部,不能随意定义变量,只能使用规定的语法定义方法和属性。
这有点使人困惑,由于类语法中使用的{}
看起来像块级做用域,但它并非。
在一个由函数构成的组件里,你就能够想怎么搞就怎么搞:
// Totally Okay: const Number = (props) => { const arrayOfNumbers = _.range(1, 10); // ... };
你能够经过 prop 属性传递一个字符串:
<Greeting name="World" />
若是须要传递一个数值,不要使用字符串:
// 不要这样作 <Greeting counter="7" />
相反,使用花括号传递一个实际的数值:
<Greeting counter={7} />
在 Greeting 组件中使用 {7}
,this.props.counter
就被赋值为数字 7
,而且能够对其进行数学运算。 若是将其做为“7”传递,而后将其视为数字,则可能会遇到意外结果。
要运行 web 服务器,须要使用主机(如 127.0.0.1 )和端口(如 8080)使服务器侦听有效 http 地址上的请求。
一旦成功运行,web 服务器就占据了那个端口,你就不能让这个端口它用,端口会被占用。
若是你尝试在另外一个终端上运行相同的服务器,将会获得端口被占用的错误提示,以下:
Error: listen EADDRINUSE 127.0.0.1:8080
请注意,有时 Web 服务器可能在后台运行或在分离的屏幕/tmux 会话中运行。你看不到,但它仍然占据了端口。 要从新启动服务器,须要“杀死”仍在运行的服务器。
要识别使用某个端口的进程,可使用 ps
之类的命令(以及关于应用程序的grep),或者若是你知道端口号,则可使用 lsof
命令:
lsof -i :8080
有些项目依赖于 shell 环境变量的存在才能启动。 若是在没有所需环境变量的状况下运行这些项目,它们将尝试为它们使用未定义的值,并可能会给你一些神秘的错误。
例如,若是项目链接到像 MongoDB 这样的数据库,则可能会使用像 process.env.MONGO_URI
这样的环境变量来链接它。 这容许项目与不一样环境中的不一样MongoDB 实例一块儿使用。
要在本地运行链接到 MongoDB 的项目,首先必须导出 MONGO_URI
环境变量。 例如,若是你在端口 27017
上运行本地 MongoDB,则须要在运行项目以前执行此操做:
export MONGO_URI="mongodb://localhost:27017/mydb"
你能够 grep 项目源代码,找到 process.env
来查清楚其运行正常所须要的环境变量。
不要用:
return { something(); };
这样用:
return ( something(); );
第一个将尝试(而且会失败)返回一个对象,而第二个将正确调用 something()
函数并返回该函数返回的内容。
由于 JSX 中的任何 <tag>
都将转换为函数调用,因此在返回任何 JSX 时都会出现这个问题。
这个问题在箭头函数的缩写语法中也很常见。
不要用:
const Greeting = () => { <div> Hello World </div> };
这样用:
const Greeting = () => ( <div> Hello World </div> );
当你使用带有箭头函数的中括号时,你就新起了一个函数的做用域。箭头函数的缩写语法不用中括号。
当你想要建立一个返回普通对象的箭头函数时,上面的花括号和圆括号问题也会让你感到困惑。
不要用:
const myAction = () => { type: 'DO_THIS' };
这样用:
const myAction = () => ({ type: 'DO_THIS'});
若是没有把对象包裹在圆括号里,你就不能使用缩写语法。实际上你会给字符串定义一个标签。
这在 setState
方法的 updater
函数中很常见,由于它须要返回一个对象。 若是要使用箭头函数语法,则须要用括号包装该对象。
不要用:
this.setState(prevState => { answer: 42 });
这样用:
this.setState(prevState => ({ answer: 42 }));
使用 React.Component
,而不是 React.component
。 使用 componentDidMount
,而不是 ComponentDidMount
。使用 ReactDOM
,而不是ReactDom
。
请注意须要的 API 大小写。 若是使用不正确的大小写,获得的错误信息可能不会很明确。
从 react
和 react-dom
导入时,确保引入正确的名称,而且使用的内容与引入彻底相同,ESLint 能够帮助你指出未使用的内容。
在处理组件属性时也会遇到这种问题:
<Greeting userName="Max" /> // 在组件内部,你须要使用 props.userName 来获取传入的值
若是你没有使用 props.userName
,而是 props.username
或 props.UserName
,你会至关于用了一个 undefined
的值。须要特别留意下这点,固然更好的是配置 ESLint,它也能指出这些问题。
在类组件中,能够定义本地 state
对象,而后使用 this
访问它:
class Greeting extends React.Component { state = { name: "World", }; render() { return `Hello ${this.state.name}`; } }
以上代码会输出 “Hello World”
。
除 state
以外,你还能够定义其它本地实例属性。
class Greeting extends React.Component { user = { name: "World", }; render() { return `Hello ${this.user.name}`; } }
以上代码也会输出 “Hello World”
。
state
实例属性是一个特殊属性,由于 React 会管理它。 你只能经过 setState
更改它,当你这样作时 React 会作出响应。
然而,定义的全部其余实例属性对渲染算法没有影响。 你能够根据须要在上面的示例中更改this.user
,而且 React 不会在React中触发渲染机制。
在闭合标签里放错 /
字符。不能否认,有时你可使用 <tag/>
,而其余时间你须要</tag>
。
在 HTML 中,有一种称为“自闭合标签”(AKA void tag)。这些是表示没有任何子节点的元素的标记。例如,img 标签是一个自闭合标签:
<img src="..." /> // 没必要使用 <img></img>
div标签能够包含子项,所以你可使用开始和结束标记:
<div> Children here... </div>
这一样适用于 Reac t组件,若是组件具备子元素,以下所示:
<Greeting>Hello!</Greeting> // 注意/字符的位置
若是组件没有子元素,能够用开始/结束标签书写,或者用自闭合标签:
// 两种方式 <Greeting></Greeting> <Greeting />
如下方式是非法的:
// 错误 <Greeting><Greeting />
若是放错放了 / 字符的位置,将收到以下错误:
Syntax error: Unterminated JSX contents
import/export
特性是 JavaScript 中的官方功能(自2015年起)。 其只是 ES2015 的特性,而且尚未在流行浏览器和最新版 Node 里面被完整支持。
React 项目的流行配置使用 Webpack 和 Babel。 二者都容许使用此特性并将其编译为全部浏览器都能理解的内容。 只有工做流中有 Webpack 或 Babel 之类的转译工具,才能使用 import/export
。
可是,在 React 打包应用程序中 import/export 不意味着能够随意使用它们! 例如,若是你还经过最新的 Node 进行服务器端渲染,会行不通,你极可能会收到“unexpected token”
错误。
要让 Node 理解 import/export
(若是你在前端使用它们就是你须要了解它们,而且也想要作 SSR 渲染的话),你须要有能够编译其的 Babel preset(像是_env_ preset),才能在Node 端运行。 你能够在开发时使用像 pm2_、 _nodemon 和 _babel-watch_的工具作到这点,,并在每次更改内容时从新启动 Node。
我把这个留到最后,由于这是一个大问题,一个很常见的问题。
你能够在 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);
我在 render
里以 this.whoIsThis
的方式调用 whoIsThis
方法,由于在 render
中,this
关键字指的是与表示组件的 DOM 元素关联的组件实例。
内部 React 确保其类方法中的 “this”
指向实例。 可是,当你使用 whoIsThis
方法的引用时,JavaScript 不会自动绑定实例。
whoIsThis
方法中的 console.dir
能够正确告诉咱们当前组件实例,由于该方法是使用显式调用方(this
)直接从 render
方法调用的。 执行上面的代码时,在控制台中看到Greeting 对象:
然而,当在延迟执行通道,例如事件处理里执行一样方法时,调用对象将再也不是显性的,console.dir
也不会打印当前组件实例。
在上面的代码中,当单击字符串时,React 会调用 whoIsThis
方法,但它不会让你访问组件实例。 这就是咱们点击字符串时获得 undefined
的缘由。 若是类方法须要访问像this.props
和 this.state
这样的属性,这会是一个问题,由于它根本行不通。
对于这个问题有不少解决方案。能够将方法包装在内联函数中,或使用 .bind
来改变 this
指向。 对于不常常更新的组件,二者均可以。
还能够经过在类的构造函数中而不是在 render
方法中执行来优化 bind
方法。 可是,此方法的最佳解决方案是经过 Babel 来使用 ECMAScript 类字段我(目前仍是stage-3),这样对于处理程序只需使用箭头函数就能够了:
class Greeting extends React.Component { whoIsThis = () => { console.dir(this); } render() { return ( <div onClick={this.whoIsThis}> Hello World </div> ); } }
这样会和预期同样执行:
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
原文:
https://edgecoders.com/react-...
你的点赞是我持续分享好东西的动力,欢迎点赞!