An easy way to name Labels and generate unique keys.
A story about Accessible Accessibility, Keys, IDs, and Reconciliation.
There are two things in React world, always hard to do, always forgotten or done wrong.
- Labels for inputs. And they are super important for usability, for UX.
- Correct
key
prop for the mapped React Components. Don’t use indexes.
Accessibility for Labels
HTML inputs are everywhere. Each site has a Form, and every Form is built from Inputs. A11y requires you to provide some human readable explanation for every input, usually in form of Label
tag connected, better say — paired, with source input. For someone those labels are the only way to understand, t see or hear your form. But, even f you are not giving a shit about “keyboard” and “blind” users— labels are playing important role for mouse users — they can just “click” on (bigger) label, to toggle checkbox.
A11y is always important. For everyone!
HTML itself gives you 2 ways to establish a link between input and label – place input inside label, or connect them by id/htmlFor.
The first ways might be not acceptable due to design. Sometimes you can’t nest input in label. In my experience — in every second case. Just often.
But the second way is also hardly doable, as long it requires unique ID to be provided for each pair.
Unique ID to be provided for each pair.
It just sounds easy, but is there any good way to hardcore unique ID beforehand? Having in mind a component approach – probably no, no way.
Any good way to generate unique, but “stable”, Id for each input+label pair? I mean — good way, not trying to generate
form4_input5_userName
each time.
Let’s try to solve another problem first.
Reconciliation
Each child in an array should have a unique «key» prop.
I hope you saw this message before. Many times. How you solved the problem? What did you provide as “the key”?
Let’s recall what we have do, and what we shouldn’t:
- We have to provide locally unique key to let React keep track on Component instances, when they change their presence or order.
- We shall not use array index, as long it represent data “position” inside array, but not “data” itself, ie not representing nor presence, nor order.
- Key should be a string. It could not be a data, even if it should.
💩! messing with keys, could lead to ☝️☝️☝️ — component recreation.
So, if you are rendering some data backed by Redux, and thus having some unique key, you probably having in your “normalized” store – all good.
If you don get the point — try read this article —
If you don’t have a key inside you data, or storing just “lists” in redux – you are doomed. We don’t have a key we could rely on. There are a lot of situations, when we don’t have a “primary key” we could use.
We don’t have a key we could rely on.
Get the key!
Key should be a string. It couldn’t be a “data”,
even if it should.
But you don’t need that key. The key is the data itself. Even more — the key could be a rendering fact! Ie the fact of mounting of some component.
Idea behind react key is simple – key should represent some data, you used to render some component.
For example — you are rendering a list of objects, and one of them is something with a key “I am the key for the third block of data you have got from backend”.
If data is not changed – key will keep it value.
Even if data is changed — key will still be the same, as long it is representing the same data as before. And even you don’t have the key
as a part of data — it doesn’t change the rules. Cos
Data is a key
And JavaScript already has a construction to map some “object” to some value – it is a WeakMap.
Sample code to use weakmaps to generate unique key for an object. For any object, hash, record, class, function, and so on.
PS: this is actual code from weak-key library. Try it out!
Just weakKey(dataElement)
to convert your “data” to an proper unique react key. Just keep in mind — it will return an unique key for each unique data
you will pass into it. Redux and other “immutable” patterns will always generate a “new” data
in case of change. So — don’t forget to use real “keys” you might have inside data, if you have them.
Anyway — one problem solved!
What about labels?
Whey need something almost similar to keys, but not for “data” — for “position” in the render tree.
Does React gives us something to solve this problem? Define a problem first, or even better — define a solution.
Each time you want to render input+label pair — acquire an unique ID, and keep it unchanged while that pair stays on the screen.
Spoiler alert – use life cycle to generate and “keep” unique id, and then use it to render “assessable” input-label pair. And then you can use power of render props to make everything a bit more easy to handle.
☝️ thats is all we need.
“Render less” containers, could empower you and the code you write. And they also could solve “id for input” problem with easy.
Read more about “keys”
Make it official.
Feel free to play with this pattern, but you want to keep using it — don’t forget about proper tests and some edge cases.
I recommend you React-uid library, as long it is already written, well tested, support SSR rendering, ie able to generate unique ids “twice”, first for server, and next for client, and has a beautiful blonde author 👩.
Wanna more details?
React-UID is not about keys for rendering arrays or pairing input with labels. It could do more. Lets’ Dive Deeper.