这是在reactjs.org/hooks文档上的第一个例子:
import {useState} from 'react'
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
这个const [count, setCount] = useState(0); ,就是我们今天要讨论的那一行。这里的语法被称为 "数组析构",它是在臭名昭著的(比臭名昭著还臭名昭著的)ES6版本中被引入JavaScript的。
我坚信
因此,当我看到我不熟悉的语法时,我喜欢阅读它,了解它如何与语言的其他部分一起工作。问题是,"谷歌 "语法可能很困难。认真地说...试着在谷歌上搜索一下语法本身,就好像你不知道它叫 "去结构化 "一样。相当困难啊所以我的诀窍是这样的。我去asexplorer.net,把我不明白的代码粘贴进去。
酷!"。Babel称这是一个 "ArrayPattern"。因此,让我们继续前进,在谷歌上搜索 "site:https://developer.mozilla.orgarray pattern"(这样,谷歌只返回MDN上的文章结果,MDN是学习JavaScript所有知识的绝佳资源)。
很好,第一个结果把我们带到了"Destructuring assignment",在那里我们可以了解到关于这个功能的所有信息(我想如果你想的话,你可以阅读这个文章而不是在这里继续阅读😅)。
通常,像这样的语法就是我们所说的其他功能的 "语法糖"。 下面是维基百科对语法糖的描述。
在计算机科学中,句法糖是编程语言中的句法,旨在使事情更容易阅读或表达。它使人类使用的语言更 "甜":事情可以表达得更清楚、更简明,或者以另一种风格表达,而有些人可能更喜欢。
好的,所以基本上它意味着在一种给定的语言中存在着写代码的常见模式或方法,所以该语言做出了一种语法特征,使这种模式需要更少的代码或更多的表达。考虑到这一点,当我学习新的语法时,我喜欢对语法进行 "去粗取精",看看如果我们没有这个功能,会是什么样子。
幸运的是,我们有Babel和TypeScript,它们可以将这种较新的语法编译成老式浏览器可以支持的东西(估计也是我们可能更熟悉的东西)。因此,我的下一步是到网上的Babel REPL去粘贴代码。下面是结果的样子:
'use strict'
var _slicedToArray = (function () {
function sliceIterator(arr, i) {
var _arr = []
var _n = true
var _d = false
var _e = undefined
try {
for (
var _i = arr[Symbol.iterator](), _s;
!(_n = (_s = _i.next()).done);
_n = true
) {
_arr.push(_s.value)
if (i && _arr.length === i) break
}
} catch (err) {
_d = true
_e = err
} finally {
try {
if (!_n && _i['return']) _i['return']()
} finally {
if (_d) throw _e
}
}
return _arr
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i)
} else {
throw new TypeError(
'Invalid attempt to destructure non-iterable instance',
)
}
}
})()
// const [count, setCount] = useState(0);
var _useState = useState(0),
_useState2 = _slicedToArray(_useState, 2),
count = _useState2[0],
setCount = _useState2[1]
😬 YIKES!嗯...好吧,有时Babel使用的工具既能使它更符合规范,又能使代码更难理解。幸运的是,在Babel Repl的 "Env Preset "中有一个叫 "Loose "的选项,可以大大简化这个输出。
// const [count, setCount] = useState(0);
var _useState = useState(0),
count = _useState[0],
setCount = _useState[1]
😌 呼,这样就好了。好的,那么这里发生了什么。Babel没有使用数组模式,而是将useState 的返回值分配给一个叫做_useState 的变量。然后它把_useState当作一个数组,把count 赋给数组中的第一个项目,把setCount赋给第二个项目。
让我们来玩一玩,探索一下这个语法。
我可以随意调用这些值吗?
// const [whateverIWant, reallyICanChooseWhatItIsCalled] = useState(0);
var _useState = useState(0),
whateverIWant = _useState[0],
reallyICanChooseWhatItIsCalled = _useState[1]
我可以添加更多的元素吗?
// const [count, setCount, somethingElse] = useState(0);
var _useState = useState(0),
count = _useState[0],
setCount = _useState[1],
somethingElse = _useState[2]
我可以抽出更少的元素吗?
// const [count] = useState(0);
var _useState = useState(0),
count = _useState[0]
我可以跳过一个吗?
// const [, setCount] = useState(0);
var _useState = useState(0),
setCount = _useState[1]
我可以跳过更多吗?
// const [,,, wow,, neat] = useState(0);
var _useState = useState(0),
wow = _useState[3],
neat = _useState[5]
我看到有人在里面放了一个奇怪的= 符号,那是做什么的?
// const [count = 3, setCount] = useState(0);
var _useState = useState(0),
_useState$ = _useState[0],
count = _useState$ === undefined ? 3 : _useState$,
setCount = _useState[1]
Oooh, fancy, 所以如果数组的第一个元素是未定义的,那么我们就把count 改为3 。默认值!很好。
注意:上面的大多数事情你都不需要用useState,因为我们总是可以依靠useState ,返回一个有两个元素的数组!我们接下来会更多地看这个。
好的,这有助于我们理解实际发生的事情。这个语法并不是React特有的。它是JavaScript规范中内置的,React的useState 钩子是利用它作为一个符合人体工程学的API机制,允许你从一个函数调用中获得两个值。很好!
好吧,那么useState 实际上是做什么的?它真正返回的是什么?它一定是返回一个数组,以便我们像这样进行数组重构,对吗?酷,让我们来看看。
有趣的是,useState 的实现存在于react-dom 中,而不是react 。我知道,这可能会让人困惑,因为我们从react 包中的import useState ,但它实际上只是委托给了当前的渲染器(在我们这里是react-dom )。事实上,setState 也是这样的!
关于useState 的另一个有趣的事情是,react-dom 的实现只有几行。
function useState(initialState) {
return useReducer(
basicStateReducer,
// useReducer has a special case to support lazy useState initializers
initialState,
)
}
😱它实际上只是一个使用了useReducer 钩子的钩子!好吧,但是basicStateReducer 是什么东西啊?
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action
}
好吧,有意思,所以useReducer 实际上有100多行代码,所以让我们看看useReducer的返回结果。
return [newState, dispatch]
看吧!它是一个数组!因此,当我们调用useState ,它将返回对useReducer 的调用,这将返回两个值的数组。这使得我们可以进行我们想要的数组解构,所以我们可以不写。
const stateAndUpdater = useState(0)
const count = stateAndUpdater[0]
const setCount = stateAndUpdater[1]
我们可以这样写:
const [count, setCount] = useState(0)
很好!
总结
我希望你觉得这篇文章对你有帮助!即使你已经非常熟悉析构语法,我上面展示的学习新语法的过程对我很有帮助,就在最近的星期五,当我在玩TypeScript的时候。 看到我不熟悉的语法和学习新的东西是我在这个行业永远不会厌倦的事情而且,学习这些语法位背后的基本原理会让你更有效地使用它们。我还应该提到,你可以用结构化做更多的事情,如果你有兴趣,在我的ES6研讨会上有一个关于结构化的部分,可以在我的YouTube频道上完全免费获得。祝您好运!