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

推荐订阅源

Spread Privacy
Spread Privacy
V
Visual Studio Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recorded Future
Recorded Future
云风的 BLOG
云风的 BLOG
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Apple Machine Learning Research
Apple Machine Learning Research
MyScale Blog
MyScale Blog
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
Recent Announcements
Recent Announcements
V
V2EX
The GitHub Blog
The GitHub Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
小众软件
小众软件
aimingoo的专栏
aimingoo的专栏
V
Vulnerabilities – Threatpost
C
Check Point Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
AI
AI
宝玉的分享
宝玉的分享
P
Proofpoint News Feed
量子位
Attack and Defense Labs
Attack and Defense Labs
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Privacy International News Feed
Google DeepMind News
Google DeepMind News
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
C
CERT Recently Published Vulnerability Notes
腾讯CDC
Latest news
Latest news
Google DeepMind News
Google DeepMind News
The Register - Security
The Register - Security
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
G
GRAHAM CLULEY
Blog — PlanetScale
Blog — PlanetScale
博客园_首页
美团技术团队
The Cloudflare Blog
T
Tenable Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
J
Java Code Geeks
SecWiki News
SecWiki News
Webroot Blog
Webroot Blog
N
News | PayPal Newsroom
博客园 - 叶小钗
博客园 - Franky

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 How to Enable React Concurrent Mode How to add testing to an existing project Profile a React App for Performance
Understanding React's key prop
2019-11-11 · via Kent C. Dodds Blog

Watch "Use the key prop when Rendering a List with React" on egghead.io (part of The Beginner's Guide to ReactJS).

Play around with this form:

Topic

Email Subject

Email body

Specifically, try changing the subject, then switch the topic and notice that the value in the input field doesn't change to a more sensible subject. Even if you type something like "My company needs training" and then changing the topic from "Training" to "Question" it would make more sense to have it reset the subject to a better default.

Now try this one:

Topic

Email Subject

Email body

That's working as expected now. Here's the implementation, and I'll highlight the difference:

const defaultValuesByTopic = {
	training: 'I would like some training',
	consulting: 'I have consulting needs',
	question: 'I have some questions',
}

function Contact() {
	const [topic, setTopic] = React.useState('training')

	return (
		<form>
			<label htmlFor="topic">Topic</label>
			<select
				id="topic"
				value={topic}
				onChange={(e) => setTopic(e.target.value)}
			>
				<option value="training">Training</option>
				<option value="consulting">Consulting</option>
				<option value="question">Question</option>
			</select>
			<label htmlFor="subject">Email Subject</label>
			<input
				id="subject"
				key={topic}
				defaultValue={defaultValuesByTopic[topic]}
			/>
			<label htmlFor="body">Email body</label>
			<textarea id="body" />
		</form>
	)
}

The only difference between these implementations is that the working one has a key prop and the other does not.

I want to share a little trick with you, not because I use this a lot (though this is exactly what I do on my contact page), but because understanding this principle will help you understand React a bit better. It has to do with React component "instances" and how React treats the key prop.


What I'm about to show you has a lot to do with element/component instances and applies just as much to <input />s like above as it does to the components you write and render. It may be a bit easier to understand with component state, so that's the angle we're going to approach this from.

Imagine you've got a React component that manages internal state. That state is attached to the component instance. This is why you can render that component twice on the page and they will operate completely independently. For our demonstration, let's use something really simple:

function Counter() {
	const [count, setCount] = React.useState(0)
	const increment = () => setCount((c) => c + 1)
	return <button onClick={increment}>{count}</button>
}

We could render this many times on the page and each would be completely independent. React will store the state with each individual instance. When one component is removed from the page, it won't affect others. If you render a new one, it doesn't affect existing components.

You may know that React's key prop is something you need to put on elements when you map over an array (otherwise React will get mad at you).

Side note: If you'd like to know why this is necessary and what can happen if you ignore it or simply put the index as the key, watch "Use the key prop when Rendering a List with React"

React's key prop gives you the ability to control component instances. Each time React renders your components, it's calling your functions to retrieve the new React elements that it uses to update the DOM. If you return the same element types, it keeps those components/DOM nodes around, even if all the props changed.

For more on this, read One simple trick to optimize React re-renders

That asterisk on the word "all" above is what I want to talk about here. The exception to this is the key prop. This allows you to return the exact same element type, but force React to unmount the previous instance, and mount a new one. This means that all state that had existed in the component at the time is completely removed and the component is "reinitialized" for all intents and purposes. For components, this means that React will run cleanup on effects (or componentWillUnmount), then it will run state initializers (or the constructor) and effect callbacks (or componentDidMount).

NOTE: effect cleanup actually happens after the new component has been mounted, but before the next effect callback is run.

Here's a simple example of this working in a counter:

function Counter() {
	console.log('Counter called')

	const [count, setCount] = React.useState(() => {
		console.log('Counter useState initializer')
		return 0
	})
	const increment = () => setCount((c) => c + 1)

	React.useEffect(() => {
		console.log('Counter useEffect callback')
		return () => {
			console.log('Counter useEffect cleanup')
		}
	}, [])

	console.log('Counter returning react elements')
	return <button onClick={increment}>{count}</button>
}

function CounterParent() {
	// using useReducer this way basically ensures that any time you call
	// setCounterKey, the `counterKey` is set to a new value which will
	// make the `key` different resulting in React unmounting the previous
	// component and mounting a new one.
	const [counterKey, setCounterKey] = React.useReducer((c) => c + 1, 0)
	return (
		<div>
			<button onClick={setCounterKey}>reset</button>
			<Counter key={counterKey} />
		</div>
	)
}

And here's that rendered out:

Here's an annotated example of what would be logged if I click the counter button, then click reset:

// getting mounted
Counter called
Counter useState initializer
Counter returning react elements
// now it's mounted
Counter useEffect callback

// click the counter button
Counter called
Counter returning react elements
// notice the initializer and effect callback are not called this time

// click the reset button in the parent
// these next logs are happening for our new instance
Counter called
Counter useState initializer
Counter returning react elements

// cleanup old instance
Counter useEffect cleanup

// new instance is now mounted
Counter useEffect callback

Conclusion

Again, this happens just as much for the state of native form elements (for things like value and even focus). The key prop isn't just for getting rid of that annoying React console error when you try to render an array of elements (all "annoying" errors from React are awesome and help you avoid bugs, so please do not ignore them). The key prop can also be a useful mechanism for controlling React component and element instances.

I hope that was interesting/enlightening. If you want to play around with any of this code, I have a codesandbox for it here. Have fun!