Magic strings create an implicit dependency between components. Implicit dependencies can make understanding how a system interoperates and the relationship between components more difficult. Where does this action come from and who else cares about this? These two "save" actions have the same name, but are they actually the same? Magic strings used like this are kind of like globals.
Rather than using magic strings, exporting and consuming a constant from outside the file it's defined in requires creating an explicit and directional relationship between the definition & the consumer: `import { CONSTANT } from './place'`.
The boilerplate is undesirable but I think showing explicit, directional relationships between files via require/import is more valuable for the overall cohesiveness of the system than the few lines of code you save.
I agree with the article that when constants are defined and consumed in the one file, constants are perhaps overkill, though maybe there's benefit to maintaining the same pattern for both internally and externally consumed constants.
Also agree with the overall sentiment of the article, should definitely be questioning and evaluating the cost benefit of best practices for our specific use-cases rather than blindly cargo-culting the dogma.
I'll often take it a step further than that... since I tend to break up my actioncreators and reducers by feature and that feature is within a redux subtree, I may
import { SAVE, SAVE_SUCCESS, SAVE_FAIL } from './constants'
but underneath it is likely...
export const SAVE = 'promise:feature/save';
export const SAVE_SUCCESS = 'resolve:feature/save';
export const SAVE_FAIL = 'reject:feature/save';
in this way I can have extensions in redux to handle 'promise:*', 'resolve:*' and 'reject:*' generically, in addition to or before the resolver.
Also worth noting that the article doesn't address the benefits listed in a link he provides near the end, namely:
- It helps keep the naming consistent because all action types are gathered in a single place.
- Sometimes you want to see all existing actions before working on a new feature. It may be that the action you need was already added by somebody on the team, but you didn’t know.
- The list of action types that were added, removed, and changed in a Pull Request helps everyone on the team keep track of scope and implementation of new features.
- If you make a typo when importing an action constant, you will get undefined. This is much easier to notice than a typo when you wonder why nothing happens when the action is dispatched.
Magic strings everywhere ? Ugh... A few more arguments for constants:
1. Autocomplete
2. Autodocumentation (basically having a list of available actions by ctrl+spacing)
3. Namespacing actions (ADD.EVENT.SUCCESS instead of "ADD_EVENT_SUCCESS")
4. Minification. It's easier to mangle constant references than magic strings
I think this is a great article 'title' but the author picked a bad example to argue for. I was really hoping to read actual examples of overcomplicated JS.
Good to think about it, and I think there are more useful topics to discuss. I always try to think of how another user may perceive/be able to understand my code.
Sorry
I stopped reading it after the first line:
"... that popularity goes to it's un-opinionated nature."
---
can we please use "its" when it's "its" that has to be used.
cheers
I appreciate that, but it cannot be a valid reason for being complacent.
And, by the way, I know many non-native speakers whose writing skills are amazing.
It's because they put the effort
I am not judging anybody, I was providing some genuine feedback. I respect that the guy is writing about JavaScript from Serbia. And I am one of the many people he can reach. So my two cents advice is too put more effort in his communication skills.