it("should work with no classes", () => {
expect(classNames({}).to.be.false
})
it("should work with a single class", () => {
expect(classNames({a:true})).to.equal("a")
})
it("should work with booleans", () => {
expect(classNames({a:true, b: false}).to.equal("a b")
})
it("should work with falsy and truthy values", () => {
expect(classNames({a: 5, b: ""}).to.equal("a b")
})
This isn't redux. It's a trivial utility function. Your kind of thinking is too often applied without thinking. Before depending on someone's code, ask yourself if you can roll your own in under 5 minutes. The answer could easily be yes. There is no good reason to depend on someone else's code if it's trivial. Having more dependencies than you need is a cardinal sin that way too many javascript developers commit.And you can't write this particular utility function in 5 minutes and support all the uses-cases, particularly if you are going to write a bunch of unit tests.
The reason to depend on someone else's code in trivial use-cases is entirely because they are trivial. There's nothing you're doing aside from wasting time by writing it. lodash is literally a massive library of mostly "trival" functions that no one is every going to write themselves because WHY waste the time?
The only cardinal sin at play here is thinking that wasting your time reinventing the wheel is time well spent. This approach is what I'd expect from either a very young developer or a very self-centered one.
No, we're talking about 2-3 line functions. Stop arguing with a straw man. You're taking what you know to be true about complex code, and then asserting that those things are also true with simple code. That doesn't work.
> And you can't write this particular utility function in 5 minutes and support all the uses-cases
The beauty is that because we are rolling our own function, we only need to support the single function signature we decide we're going to stick to in our codebase. Supporting every use-case becomes a moot point.
The author of the library has to support the 10^5 people who use his code, meaning he's more likely to merge silly pull requests like "should take objects", "should flatten the input array", "should allow multiple arguments", "should work with X", "should do Y" because people have different styles.
> The reason to depend on someone else's code in trivial use-cases is entirely because they are trivial. There's nothing you're doing aside from wasting time by writing it
It only takes time if you are not able to do basic programming. I find basic programming to be a much more enjoyable and productive approach than depending on potentially dozens of trivial libraries whose APIs are not the way I would design them, and could change at any time. Learning dozens of APIs takes just as much time as implementing them for a certain set of functions. That set is different for everyone, but it is not the null set for anyone.
The library in question is literally 50 lines of code. About 30 when you remove the exports and comments at the top. I find it somewhat ironic that you are so opposed to using utility libraries and you didn't even bother to look at the one in play here.
> The beauty is that because we are rolling our own function, we only need to support the single function signature we decide we're going to stick to in our codebase. Supporting every use-case becomes a moot point.
No it doesn't. All the use-cases are useful. You use strings when you need default classes and objects when you have conditional classes. You can't implement both of those in two lines of code.
> The author of the library has to support the 10^5 people who use his code, meaning he's more likely to merge silly pull requests like "should take objects",
It's always taken objects as properties and for good reason: it's a clean way to separate conditional classes from unconditional ones.
But sure dude, all those features are insane. Put his code up to a solid 30 lines. Really saving the bacon by rolling your own that doesn't do the same thing.
> Learning dozens of APIs takes just as much time as implementing them for a certain set of functions. That set is different for everyone, but it is not the null set for anyone.
Yeah, that's why everyone writes their own lodash. It's just a set of trivial functions.
Why does everyone in JS land treat dependencies like they're just some free magic, as if an intimate familiarity with the source code is injected into your entire team's brains the moment you type 'npm install'? I've worked with far too many people that blow out the package.json file for every little thing then wonder how every other dev on their team seems to be paralysed by the smallest task.
Every time someone on your team runs into a dependency boundary they need to stop reading code, pull up a browser, and start reading docs. You can't trace the path of execution through a black box dependency, which means if you use a bunch of them in one place your developers are going to have to hold a hell of a lot of information in their head while they analyse that code. If they drop something, it's back to the docs again. At the extreme, it's a context switching nightmare.
Dependencies cost time, effort and brain power. Make sure you're getting enough in return.
Dependency boundary? If you can't figure out what a function called classNames does in the context of a React render function in which the output of classNames is put as a value in to "className" attributes, you need to find a new job.
Dependencies cost time, effort and brain power when they actually require those things. Be thoughtful when using something that actually requires investigation. But honestly, a trivial function should be self-documenting or it really isn't trivial, is it? If I had an npm library called array-flatten are you seriously going to read the docs, or assume it flattens nested arrays?
Of course it's easier to read a trivial function than the docs for a trivial function. That's what makes it trivial, it's not abstracting away anything meaningful. And of course I can easily figure out what classNames outputs.
You know what I can't figure out trivially? What inputs it takes. It's a polymorphic function. Me and Bob load up a new codebase, I open a few files and see classNames used a couple of times with an object parameter. I can now be pretty confident what the API of the function is. Bob opens up a couple of different files and sees it used with an array parameter a few times. Now he's pretty confident he's got the hang of it too. We both go off to write new components. I've got a bunch of this everywhere:
{ foo: true, bar: true, baz: true }
And Bob has a bunch of this everywhere:
[foo ? 'bar' : null, baz ? 'boop' : null]
Bam, inconsistent code. Could have easily been solved by reading the docs before using something though. And hey, at least we'll both remember for next time...
...Unless our team imports 100 of these libraries and tries to enforce a consistent use of them across the project.
There's only so much API surface area one dev can keep at the front of their mind while coding. Good devs understand this and minimize it. Bad devs pile in trivial dependencies because they don't understand their cost.
Oh, and how do I know whether flattenArray is recursive or not? What if it is and I only want to flatten one level? Does it take a second integer parameter? Or an options object? Since we're talking about JS, does it flatten Array-like objects? Is that the same API or a different one? It's almost like your example proves my point...
Yeah, and in CR you should be told not to use the object form when you have "true" as the value, as it's pointless. That's what string form is for. The "docs" are literally 5 lines where you learn you can send object literals, arrays and strings.
> There's only so much API surface area one dev can keep at the front of their mind while coding. Good devs understand this and minimize it. Bad devs pile in trivial dependencies because they don't understand their cost.
Yes, because if you implement a similar feature the remaining members of your team all know it through osmosis. The surface area of these functions is tiny, but apparently your implementation requires no investigation by anyone.
> Oh, and how do I know whether flattenArray is recursive or not?
Holy cow, man. Do you literally hand-implement EVERY utility function you've ever needed?
> It's almost like your example proves my point...
Your point seems to be: you can't manage to infer ANYTHING from a function signature AND that apparently somehow your team members can ALWAYS infer everything needed from any function YOU write.
Use code inspection and you'll never open the docs for flattenArray or anything like it again. If this is the amount of effort required for flattenArray for you, how to you actually use large libraries with HUGE surface areas?
What a dumbass argument. So instead of writing a one line abstraction, I should invest a bunch of CR time explaining to my whole team which of 3 different APIs they should be using in this dependency I've brought in?
> Holy cow, man. Do you literally hand-implement EVERY utility function you've ever needed?
No, just the trivial ones. Have you been actually reading this thread?
> Your point seems to be: you can't manage to infer ANYTHING from a function signature
No, my point is that you can't infer the full API of a function from the arguments passed to it's invokation when the function is polymorphic.
> AND that apparently somehow your team members can ALWAYS infer everything needed from any function YOU write.
If the function is trivial, which is exactly what we've been talking about this whole time, then No. Fucking. Shit. The function is trivial and they have the source code right there.
It's not a one-liner because it doesn't support the same feature set. It supports a basic use case that may be great for trivial apps, but in the real world it isn't.
> No, just the trivial ones. Have you been actually reading this thread?
And said trivial ones must still be "learned" by your co-workers. You've saved exactly zero time.
> No, my point is that you can't infer the full API of a function from the arguments passed to it's invokation when the function is polymorphic.
You can with a glorified text-editor these days with Code Inspection. I guess that's advanced stuff we shouldn't expect our co-workers to use?
> If the function is trivial, which is exactly what we've been talking about this whole time, then No. Fucking. Shit. The function is trivial and they have the source code right there.
Yet, if the function is trivial AND is imported via npm, it suddenly requires reading docs. On one hand you argue "triviality requires reading docs" and then suggest that you can implement a similar trivial function and everyone can just look at the source code.
And you don't see the irony here.
What I'm saying is that it's EASIER to understand the source code than it is to understand the documentation for classnames. That's because it abstracts nothing away. It's replacing one simple API (template strings) with another one that's very similar and of roughly the same power. By turning it into a dependency you're adding an artificial barrier between it and your code base, that makes it harder for everyone to grok. Not much harder, but enough to be a pain in the ass in any codebase where that same tradeoff is made over and over.
If you can't understand that, then I don't know what to do for you. Keep writing shit JS code and making the web worse than it already is.
it("should work with booleans", () => {
expect(classNames({a:true, b: false}).to.equal("a b")
})
Shouldnt this just equal "a" though? See, already a bug in your homegrown solution!A few people in this thread have regurgitated the same reasoning you have. The problem is that your reasoning is NOT an invariant under complexity. This reasoning does not apply nearly as much to trivial utility functions such as:
const isObj = o => !!o && typeof o === "object"
and classNames, etc. as it does to larger, more complex code.> will consult you about your intentions when you wrote the code etc.
No, no they won't. No one will consult me on what my 2-line classNames functions does. It's 2 non-obfuscated lines. These aren't undergrads in CS101.