Small plans for plurality in react and ts
The plan for plurality
Pre-emptive pluralization is described by swyx here. The TLDR is that we should try to assume plurality where we can since going from plural to handle singular is easier than the reverse.
Here's a few concrete examples of quick wins that can pre-emptively pluralize your react code.
Named props
There's a few ways to declare functions.
Option 1) There's ordered functions:
function doWork(first: string, second: number, third?: boolean) {}
Option 2) the next version is a single prop with everything:
function MyComponent({ first, second = 5, third,}: { first: string second?: number third?: boolean}) { //..}
Option 3) The last is a mix of the two:
type Props = { first: string options: { second: number; third?: boolean }}function MyComponent( first: string, options: { second: number; third?: boolean }) { //..}
Which do we choose and why? In almost all cases, it's easier to start with and never change option 2) a single prop with everything. This has the advantages of:
- props can be declared out of order
- when calling the function, the props are readable by the user to see what they are
- can help with debugging because what if the parameters are all the same type? passing in
({ orderId, customerId })
can be safer than(customerId, orderId)
if the function is typed(orderId: string, customerId: string) => null
- they work more similarly to react components
- default params don't need to come last
- there's no magic prop order to learn
The only downside is that it's a bit more verbose and the typing is a duplicate. Copilot can help eliminate the duplication altogether.
Why avoid the third with a convenience mandatory and then options next? Just going by react-query's recent move they shifted to having named parameters for everything. This helps clarify the intent and makes it easier to add new options. It's also easier to add new options without breaking existing code.
The other reason to start with option 2) is that it takes away the guesswork, should I start with ordered then move to plural? What's the magic number where it's better? Do I really want to refactor when I come in next time and try to replace all the instances where this is called? What do I do when I want to make one of the first parameters optional?
Start with a single prop and don't look back.
Named exports
This is a bit of a weird one but it's worth doing. If you have a file with a single export, it's worth naming it. This is because if you ever need to add a second export, it's as easy as adding a second export. If you don't name it, you need to go back and change all the imports. This is a bit of a pain and it's easier to just name it from the start. They also have the advantage of not forcing the user to guess the filename and can help with autocomplete (but this is better now with many editors).
Popular libraries that have started with defaults and moved to named include zod
, immer
and even react
.
Default exports are the most useful for frameworks where the default indicates something of importance such as the page, if this isn't you, it's better to start pluralized here.
// badexport default const Something = () => {}// preferexport function Something() { }// importimport { Something } from './something'
React says this about exports:
People often use default exports if the file exports only one component, and use named exports if it exports multiple components and values
However, we as developers have a lot of decisions to make and starting with named exports means we can always have the same rule and never have to refactor when we go from default to named. Predicting the future is hard and I like having the decision that supports faster iteration. As an added bonus, you don't need to type default
! Win win!
Things that might be arrays
This one is actually a little harder to do. When building an api it's sometimes tempting to return just the singular version of an item. /api/items/:id
. But in reality it's probably good to plan for an array of items /api/items?ids=
. This is because it's easier to go from an array to a single item than the other way around. It's also easier to add a second item to an array than to change a single item to an array. The plural case also handles the singular case.
Apis out of your control may have undefined
or null
This is plan for the plurality of undefineds. In js it's typically better to use optional chaining to access anything outside of the direct control of the current function and especially for any api out of one's control. The easiest way to do that is to use optional chaining where the data?.item?.value
is accessed rather than throwing and making the entire application unusable for an unplanned undefined. Usually it's better to have most of the app work than just a subsection. If it's really important to have errors when this happens, use zod to define the schema and throw an error if it's not valid.
Use js as config
What we've seen is that most of the larger js tooling that uses config ends up moving towards a .js
or ts
file at least as an option rather than just a pure .json
file. The reason for this is the plurality of environments and configuration. This is actually relatively easy to do up front by using something like cosmicconfig. Here we can provide the options of ts, js, mjs, and even yaml for yaml lovers (all 3 of you). Examples of this are eslint, babel, webpack, prettier and tailwind. These files allow imports, plugins, dynamic setup, and code based configuration and merging of external config. This is a huge win for plurality by just using cosmicconfig.
Requests
I bet you didn't think we could fit @tanstack/react-query into this, but there's a subtle plurality hidden within this that we can't get by managing requests ourselves and that's out of order requests (race conditions) as well as automatic retries for network failures. TkDodo's blog post on this goes in depth.
Conclusion
There's a lot of quick wins that don't cost much in the land of preemptive pluralization. It's worth doing them early and often. It's also worth doing them even if you don't think you'll need them. In many cases it can be easier to go from plural to singular than the other way around so starting that way can save time in the long run.