Logo

dev-resources.site

for different kinds of informations.

Why React Needs Keys, Why It Matters

Published at
7/22/2021
Categories
react
javascript
webdev
reason
Author
levinhphuc91
Categories
4 categories in total
react
open
javascript
open
webdev
open
reason
open
Author
12 person written this
levinhphuc91
open
Why React Needs Keys, Why It Matters

Everyone uses React library all know that whenever we use a map to render, we need to pass the key. Otherwise, React will "yell" at us like this.

Screenshot 2021-07-16 at 10.34.55 PM

Oh nooo šŸ˜±šŸ˜±, we need to pass the key now and most of the time we will pass like this.

list.map((l, idx) => {
Ā  Ā  return (
Ā  Ā  Ā  Ā  <div key={idx}>
Ā  Ā  Ā  Ā  Ā  Ā  <span>{l}</span>
Ā  Ā  Ā  Ā  </div>
Ā  Ā  )
})
Enter fullscreen mode Exit fullscreen mode

Problem solved āœ…, no more warning šŸ˜Ž

But...šŸ˜¢šŸ˜¢

Life is not a dream

Let jump into an example of why "Like is not a dream"

Our manager gives us a task to create a dynamic Form with multiple Input fields, the user is able to enter their information and allow us to add or delete Input.

1626447469518_ae13dc59ab0162a034b12026de911944

So

  • We already know how to render the map in React library āœ…
  • We already know how to use useState in React Hook with an array āœ…
  • We also know method push/splice on array āœ…

Easy one lah šŸ˜Ž...

Without the beat, we set up our application

export default function App() {
const [list, setList] = useState([]);

const handleDelete = (idx) => {
Ā  Ā  list.splice(idx, 1);
Ā  Ā  setList([...list]);
};

const handleAdd = () => {
    list.push(`Information ${list.length + 1}`);
Ā  Ā  setList([...list]);
};

const handleChange = (idx, l) => {
Ā  Ā  list[idx] = l;
Ā  Ā  setList([...list]);
};

return (
Ā  Ā  <div className="App">
Ā  Ā  Ā  Ā  {list.map((l, idx) => {
Ā  Ā  Ā  Ā  Ā  Ā  return (
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  <div key={idx}>
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  {FancyComponent(l, handleChange, idx)}
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  <button onClick={() => handleDelete(idx)}>Delete</button>
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  </div>
Ā  Ā  Ā  Ā  Ā  Ā  );
Ā  Ā  Ā  Ā  })}
Ā  Ā  Ā  Ā  <button onClick={() => handleAdd()}>Add</button>
Ā  Ā  </div>
Ā  Ā  );
}

const FancyComponent = (l, handleChange, idx) => {
Ā  Ā  const onChange = (e) => {
Ā  Ā  Ā  Ā  e.preventDefault();
Ā  Ā  Ā  Ā  handleChange(idx, e.currentTarget.value);
Ā  Ā  };
Ā  Ā 
Ā  Ā  return <input defaultValue={l} onChange={(e) => onChange(e)} />;
}
Enter fullscreen mode Exit fullscreen mode

Here is Playground

Done!!! Feel super awesome šŸ˜šŸ’Ŗ, it works like charm. We pass to our manager, our manager like šŸ¤©šŸ˜

A few moment later, our manager comes back again. We thought he would asking for the beer to appreciate but he said he found a bug šŸ˜±šŸ˜­. The deletion didn't work as expected šŸ„ŗ

1626448892647_303ec7c0d81218c121096366804e5f49

After he modified Information 3 to Information 323, deleted this input but the application deleted Information 4, Information 5

We like whattt šŸ˜±šŸ˜±šŸ˜±, how it could be possible!!!, it just only 50 lines of codes...

After put the console.log everywhere, googling, StackOverflow... We found the problem is the index we passed before is changed it makes ReactJS confused and render incorrect.

And if we don't use idx anymore, use content instead. Problem solved! āœ…

<div className="App">
Ā  Ā  {list.map((l, idx) => {
Ā  Ā  Ā  Ā  return (
Ā  Ā  Ā  Ā  Ā  Ā  <div key={l}>
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  {FancyComponent(l, handleChange, idx)}
Ā  Ā  Ā  Ā  Ā  Ā  Ā  Ā  <button onClick={() => handleDelete(idx)}>Delete</button>
Ā  Ā  Ā  Ā  Ā  Ā  </div>
Ā  Ā  Ā  Ā  Ā );
Ā  Ā  Ā })}
Ā  Ā  <button onClick={() => handleAdd()}>Add</button>
</div<
Enter fullscreen mode Exit fullscreen mode

PS: Still has anĀ issue, waiting for anyone to figure it out. šŸ˜‰

So now you might wonder that it is React's bug?!. No, it is not React's bug. Let's deep dive into it

If you noticed that when we change the value of the input, it works perfectly but deleting didn't work well because "changing" and "moving" state it is very different. When "moving" React needs to know what key of component is which we passed as an index of the array and deleting is changing the index hence it makes ReactJS confused. So why React don't make the by itself.

Dan's explanation:

React canā€™t make up a good key itself. Only you know how your data is structured and whether two circles in two renders are ā€œthe sameā€ circle conceptually (even if all of its data changed) or not. Usually, youā€™d use an ID generated during data creation. Such as from a databaseReact canā€™t make up a good key itself. Only you know how your data is structured and whether two circles in two renders are ā€œthe sameā€ circle conceptually (even if all of its data changed) or not. Usually, youā€™d use an ID generated during data creation. Such as from a database

It is 100% true, as an example, we go through above that it is a business requirement, as library React doesn't know what the key should be which only us, developers created those components.

What happens if you pass a random key every time?

Well, youā€™re telling React that circles are never the same between renders. So if you have some state inside of them, it will be lost after every re-render. React will destroy and recreate every circle because you told it to.

We will lose the "beauty" of ReactJS, isn't it? I believe no one wants to re-render every single time unless you have special requirements.

You might wonder why this topic pops up now, the concept of Keys has been there for a long time. ReactJS core team has started to built-in deep support Animation so Keys will play a big role there and in the future.

Bonus:

The article is inspired by Dan Abramov https://twitter.com/dan_abramov/status/1415279090446204929

reason Article's
30 articles in total
Favicon
Top 6 Reasons to Partner with Experts in Mobile App Design and Backend Development
Favicon
South Delhi Real Estate: Unveiling the Charm of Residential Houses
Favicon
How Reading Can Polish Your Learning skills
Favicon
Reason and React Meta-Frameworks
Favicon
NextJS, the App Router and ReasonReact
Favicon
ReasonReact, Auth0 and 3rd Party React Components
Favicon
Getting started with ReasonReact and Melange
Favicon
Top 9 JavaScript Flavours
Favicon
React Memory Leaks: what, why, and how to clean them up!
Favicon
The 3 Main Reasons Test Automation Projects Fail
Favicon
Comparison of Type Systems in Front-end Languages: Algebraic data types
Favicon
Editor Support for ReasonML in VSCode with Melange
Favicon
Writing Elm Ports in ReScript - 0.3
Favicon
5 Reasons That Make React Native Better Than Flutter
Favicon
Async await like syntax, without ppx in Rescript !!!
Favicon
Why React Needs Keys, Why It Matters
Favicon
Using `let.opt` in Rescript with latest Reason/OCaml
Favicon
From Reason/React to Rescript/React, Guaranteed Uncurrying
Favicon
Awesome list in rescript
Favicon
JavaScript file watching with Reason & Rescript in Dune
Favicon
Hooray!
Favicon
Getting started with ReScript and parcel
Favicon
Reasons Why Digital Healthcare Startups Meet Failures
Favicon
The strongest hearts of Rwandans
Favicon
Setting Up Webpack for ReScript
Favicon
npx resyntax
Favicon
Ethicode Projects: Contributio #1
Favicon
Displaying Notifications in ReScript
Favicon
How does ReScript affect me?
Favicon
Beating the Drom

Featured ones: