惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
T
Threat Research - Cisco Blogs
Simon Willison's Weblog
Simon Willison's Weblog
AWS News Blog
AWS News Blog
P
Privacy & Cybersecurity Law Blog
H
Help Net Security
云风的 BLOG
云风的 BLOG
G
GRAHAM CLULEY
Spread Privacy
Spread Privacy
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
A
Arctic Wolf
Project Zero
Project Zero
Engineering at Meta
Engineering at Meta
P
Privacy International News Feed
Blog — PlanetScale
Blog — PlanetScale
Stack Overflow Blog
Stack Overflow Blog
M
MIT News - Artificial intelligence
The Register - Security
The Register - Security
Recorded Future
Recorded Future
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
C
Cisco Blogs
PCI Perspectives
PCI Perspectives
Recent Announcements
Recent Announcements
Martin Fowler
Martin Fowler
A
About on SuperTechFans
W
WeLiveSecurity
GbyAI
GbyAI
V
Vulnerabilities – Threatpost
The GitHub Blog
The GitHub Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Check Point Blog
Y
Y Combinator Blog
月光博客
月光博客
Scott Helme
Scott Helme
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
U
Unit 42
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Threatpost
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Google Online Security Blog
Google Online Security Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Cisco Talos Blog
Cisco Talos Blog
博客园 - 三生石上(FineUI控件)
Hugging Face - Blog
Hugging Face - Blog
MongoDB | Blog
MongoDB | Blog
博客园 - 司徒正美

Kent C. Dodds Blog

Implementing Hybrid Semantic + Lexical Search Simplifying Containers with Cloudflare Sandboxes Migrating to Workspaces and Nx Offloading FFmpeg with Cloudflare Building Semantic Search on my Content Helping YOU ask ME questions with AI How I used Cursor to Migrate Frameworks The Dow's Start on the Covenant Path 2025 in Review The next chapter: EpicAI.pro AI is taking your job How I increased my visibility Launching Epic Web 2023 in Review Stop Being a Junior RSC with Dan Abramov and Joe Savona Live Stream Fixing a Memory Leak in a Production Node.js App 2022 in Review My Car Accident I Migrated from a Postgres Cluster to Distributed SQLite with LiteFS I'm building EpicWeb.dev A review of my time at Remix Remix: The Yang to React's Yin How I help you build better websites Why I Love Remix The State Initializer Pattern How to React ⚛️ Get a catch block error message with TypeScript Building an awesome image loading experience How Remix makes CSS clashes predictable Introducing the new kentcdodds.com How I built a modern website in 2021 How to use React Context effectively Static vs Unit vs Integration vs E2E Testing for Frontend Apps The Testing Trophy and Testing Classifications Array reduce vs chaining vs for loop Don't Solve Problems, Eliminate Them Super Simple Start to Remix Super Simple Start to ESModules in Node.js JavaScript Pass By Value Function Parameters How to write a Constrained Identity Function (CIF) in TypeScript How to optimize your context value How to write a React Component in TypeScript TypeScript Function Syntaxes Listify a JavaScript Array Build vs Buy: Component Libraries edition Using fetch with TypeScript Wrapping React.useState with TypeScript Define function overload types with TypeScript 2020 in Review Business and Engineering alignment Hi, thanks for reaching out to me 👋 useEffect vs useLayoutEffect Super simple start to Firebase functions Super simple start to Netlify functions Super Simple Start to css variables Favor Progress Over Pride in Open Source Testing Implementation Details How getting into Open Source has been awesome for me useState lazy initialization and function updates Use ternaries rather than && in JSX Application State Management with React Use react-error-boundary to handle errors in React JavaScript to Know for React How I structure Express apps What open source project should I contribute to? When I follow TDD AHA Programming 💡 How I Record Educational Videos Should I write a test or fix a bug? Stop mocking fetch Intentional Career Building Improve test error messages of your abstractions Tracing user interactions with React Eliminate an entire category of bugs with a few simple tools Common mistakes with React Testing Library Super Simple Start to React Stop using client-side route redirects The State Reducer Pattern with React Hooks Function forms Replace axios with a simple custom fetch wrapper How to test custom React hooks React Production Performance Monitoring Should I useState or useReducer? Stop using isLoading booleans Make Your Test Fail Make your own DevTools An Argument for Automation Fix the "not wrapped in act(...)" warning Super Simple Start to ESModules in the Browser Implementing a simple state machine library in JavaScript 2010s Decade in Review Why users care about how you write code Why I avoid nesting closures Don't call a React function component Why your team needs TestingJavaScript.com Inversion of Control Understanding React's key prop How to Enable React Concurrent Mode Profile a React App for Performance
React Hooks: Array Destructuring Fundamentals
2018-12-31 · via Kent C. Dodds Blog

This is the first example on the https://reactjs.org/hooks documentation:

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>
	)
}

That const [count, setCount] = useState(0); is the line we're going to be talking about today. The syntax here is called "array destructuring" and it was introduced into JavaScript in the infamous (more than famous) ES6 release.

I'm a firm believer that:

The better you understand an abstraction, the more effective you will be at using it. – me, literally right when I wrote this...

So when I see syntax that I'm unfamiliar with, I like to read about it and understand how it works with the rest of the language. The problem is that it can be difficult to "Google" syntax. Seriously... Try Googling the syntax itself as if you didn't know that it's called "destructuring." Pretty tough! So here's my trick. I go to astexplorer.net and paste in the code that I don't understand:

ASTExplorer.net with the code showing ArrayPattern

Cool! Babel calls that an "ArrayPattern." So let's go ahead and Google for that. We'll search for "site:https://developer.mozilla.org array pattern" (that way Google only returns results for articles on MDN which is a terrific resource on learning everything there is to know about JavaScript).

Sweet, the first result takes us to "Destructuring assignment" where we can learn all about this feature (I guess you can read that instead of continuing here if you want to 😅).

Often syntax like this is what we call "syntactic sugar" for other features. Here's what wikipedia says about syntactic sugar:

In computer science, syntactic sugar is syntax within a programming language that is designed to make things easier to read or to express. It makes the language "sweeter" for human use: things can be expressed more clearly, more concisely, or in an alternative style that some may prefer.

Ok, so basically it means that there are common patterns or ways to write code in a given language, so the language makes a syntax feature to make that pattern require less code or more expressive. With this in mind, when I'm learning new syntax, I like to "de-sugar" the syntax to see what it would look like if we didn't have that feature.

Luckily for us, we have Babel and TypeScript which can compile this newer syntax into something older browsers can support (and presumably to something we may be more familiar with). So my next step is to go to the online babel REPL and paste in the code. Here's what the result looks like:

'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! Hmmm... Ok, so sometimes Babel uses utilities which both make it more compliant to the specification, but also can make the code a little harder to understand. Luckily, there's an option on the Babel Repl's "Env Preset" called "Loose" which will simplify this output considerably:

// const [count, setCount] = useState(0);
var _useState = useState(0),
	count = _useState[0],
	setCount = _useState[1]

😌 Phew, that's better. Ok, so what's going on here. Babel's taking our one line and rather than using the Array Pattern thing, it's assigning the return value of useState to a variable called _useState. Then it's treating _useState as an array and it assigns count to the first item in the array and setCount to the second one.

Let's play around with this a little bit to explore the syntax:

Can I call the values whatever I want?

// const [whateverIWant, reallyICanChooseWhatItIsCalled] = useState(0);
var _useState = useState(0),
	whateverIWant = _useState[0],
	reallyICanChooseWhatItIsCalled = _useState[1]

Can I add more elements?

// const [count, setCount, somethingElse] = useState(0);
var _useState = useState(0),
	count = _useState[0],
	setCount = _useState[1],
	somethingElse = _useState[2]

Can I pull out fewer?

// const [count] = useState(0);
var _useState = useState(0),
	count = _useState[0]

Can I skip one?

// const [, setCount] = useState(0);
var _useState = useState(0),
	setCount = _useState[1]

Can I skip more?

// const [,,, wow,, neat] = useState(0);
var _useState = useState(0),
	wow = _useState[3],
	neat = _useState[5]

I saw someone put a weird = sign in there, what does that do?

// const [count = 3, setCount] = useState(0);
var _useState = useState(0),
	_useState$ = _useState[0],
	count = _useState$ === undefined ? 3 : _useState$,
	setCount = _useState[1]

Oooh, fancy, so if the first element of the array is undefined, then we'll set count to 3 instead. Default values! Sweet.

Note: most of the things above you would never need to do with useState because we can always rely on useState returning an array of two elements! We'll look at that more next.

Ok cool, so this helps us understand what's actually going on. There's nothing React-specific about this syntax. It's built-into the JavaScript specification, and React's useState hook is leveraging it as a mechanism for an ergonomic API that allows you to get two values out of a single function call. Neat!

Ok, so what does useState actually do then? What is it really returning? It must be returning an array for us to be doing the array destructuring like this right? Cool, let's check that out.

One thing that's interesting is that the implementation of useState exists within react-dom rather than react. I know, that may be confusing because we import useState from the react package, but it actually just delegates to the current renderer (which is react-dom in our situation here). In fact, setState is the same way!

Another interesting thing about useState is that the implementation in react-dom is just a few lines:

function useState(initialState) {
	return useReducer(
		basicStateReducer,
		// useReducer has a special case to support lazy useState initializers
		initialState,
	)
}

😱 it's actually just a hook that's using the useReducer hook! Ok, but what is that basicStateReducer thing huh?

function basicStateReducer(state, action) {
	return typeof action === 'function' ? action(state) : action
}

Ok, interesting, so useReducer is actually over 100 lines of code, so let's just look at what useReducer returns:

return [newState, dispatch]

See! It's an array! So when we call useState, it returns a call to useReducer which will return an array of two values. This allows us to do the array destructuring that we want so instead of writing:

const stateAndUpdater = useState(0)
const count = stateAndUpdater[0]
const setCount = stateAndUpdater[1]

We can write:

const [count, setCount] = useState(0)

Nice!

Conclusion

I hope you found this one helpful! Even if you already are very familiar with destructing syntax, the process of learning new syntax I show above has been helpful to me as recently as Friday when I was playing around with TypeScript. Seeing syntax that I'm not familiar with and learning new things is something that I'll never get tired of in this industry! And learning the fundamentals behind these bits of syntax will make you more effective at using them. I should mention also that there are more things you can do with destructuring and if you're interested there's a section about destructuring in my ES6 workshop that's available completely free on my YouTube channel. Good luck!