My ongoing frustration with code reuse in general is that the code I'm looking to reuse reuses other code that reuses other code that refuses other code that reuses... it's turtles all the way down, and any of those turtles can throw a completely opaque error message that required very deep knowledge of that particular turtle to make any sense of. I applaud, thank, and appreciate the author for going back to basics.
I don't really mind the small standard library with small modules approach, but JS is the worst kind of language to use that model. It's just too dynamic and with too many gotchas. You can monkey patch anything, you can mix optional arguments with required arguments, you can pretty much do anything you want with someone else's code. And the structure of package management systems like NPM and the resulting culture only exacerbate the crappiness of it all. And JS library authors take full advantage of this.
If you want the small stdlib with small module approach, you need a language and runtime system with some better static guarantees.
Doesn't npm manage transitive dependencies, just like Maven in Java-land? I sure thought it did. Are package maintainers just not getting their dependencies right? E.g. - "everybody" uses jQuery, it will just be there, I'm not going to list it.
I have only used Node/NPM a little bit at home. Most of my JS work has been some things embedded within larger Java web apps, and my coworkers don't really like JS (they are wanting to do static-OOP where dynamic-FP fits better).
> Doesn't npm manage transitive dependencies, just like Maven in Java-land?
It does - actually the pain that I'm expressing is the same pain I felt with Maven: somebody comes along and says, "Oh, you're doing OAuth? You should definitely use an OAuth library for that." So I pull in the OAuth library and let either mvn or npm pull in all of the transitive dependencies, half of which I've never heard of and the other half I know, but am not an expert in. Which is a problem when one of them reports an error message like "Unrecognized parameter in dozer mapping". What? What parameter? What mapping? WTF is 'dozer'? I google the error message and get a hundred hits, none of which have anything to do with my case. So now I'm going through a poorly documented transitive dependency trying to figure out a) what it does b) how it does it and c) why it broke my OAuth implementation (all with the boss standing behind me, checking his watch, tapping his foot impatiently, wondering why this is taking so long when I'm reusing something that's supposed to make this all magically work instantaneously). I'm suddenly loading somebody else's source code in a debugger, trying to figure out where it gets all its configuration data, thinking "would it _really_ take that much longer to write my own OAuth.... (or date picker or JSON parser or whatever else is misbehaving)"
An example of a current problem in NPM land, is "peer dependencies". Suppose 30 of your direct and indirect dependencies require one popular, fast moving library, say, React. For safety's sake, and to spare the effort of regression testing, many of these modules specify a fairly conservative version range for their own React dependency. Normally, NPM would just give you multiple versions of React, and each library would get the React version that it expects.
There is a small problem and a big problem with this. The small problem, is that your build size will be unreasonably large. The big problem, is that this React library has internal state - you can't just initialise multiple copies of it, and expect them to share a single state.
Enter peer dependencies. Each library that needs React, demands that the root module, your application, specify a normal depency on React, that satisfies their version range requirements. Clearly, this quickly becomes impossible. So, version mismatch is treated as a warning, not an error, and you (the application developer) learn to basically ignore that warning.
I can think of lots of solutions to this problem, all of which have problems of their own.
- All libraries factor out their state, so that they are stateless. Now you can load multiple instances of a module, but if they have different major versions, then they are totally going to mess up that state that you are holding for them.
- Application developer: it is your responsibility to patch your dependencies, and their dependencies, so that they all accept an overlapping range of React versions. But who has time to test 30 modules for regressions?
- Just build more, smaller, simpler applications. This is where I am right now. I still get version warnings, but it is a little less risky to ignore them.
How do you handle this issue in the Java community?
Luck, mostly. However, libraries do tend to fall into 2 categories: small, and big. (yeah, I worked hard to come up with those)
* Small, sharp tools (sometimes) - things like Google Guava "collections" utilities, or the Ostermiller CSV file handling library, which have very small and stable scope.
* Big collections of things, such as Spring or Struts. Regardless of how you feel about these libraries, they do tend to come as a family of interrelated libraries (supplemental components) that get released in lock-step and play nice together.
I dislike the Java language, but I guess it is nice that most of the stuff on Maven Central does tend to be stable.
Java-land has been going for about 20 years, and went through a bit of a house-cleaning about 10 years ago around the time the Maven project started.
Javascript, a la Web 2.0, has really only been a serious development thing for about 10 years. It's due for its own settling in period soon, I would imagine.
> if your program uses 4 npm packages and they all list jquery as a dependency, you now have 4 versions of jquery installed on your machine
This is not true in npm v3: it now flattens the dependency tree and only duplicate dependencies if there are incompatible versions required.[1] Granted, if those four packages all depended on jQuery locked to different specific versions, then you would end up with four nested versions of jQuery.
> and any of those turtles can throw a completely opaque error message that required very deep knowledge of that particular turtle
It seems odd to bring up Java here, but this is one of the problems solved (not without cost) by checked exceptions.
By making the types of exceptions explicit, each layer is encouraged to reframe (or wrap) the errors from its dependencies in a way which matches its own usage and level of abstraction.
As someone who writes React every day, I'm not sure how the `react-dragula` implementation even remotely constitutes React integration? AFAIK React never made any guarantees about `data-reactid` nor what it means to remove it? It's not a public API and is not even used in the latest React anymore except for picking up SSR'd markup? I have serious doubts and wouldn't go anywhere near this.
Secondly, if you tout "React integration" people are going to want a Draggable component or mixin/HOC or whatever. Making people do things in `componentDidMount` isn't really integration.
That said: yes, it's a good idea to base your framework-specific integration on top of a generic battle-tested library. This however appears to be a bad example of that.
I like this approach, but one problem with plain javascript that I haven't heard many people address is testability. A module I am using with angular has lots of calls to setTimeout, but these timed out functions are sometimes not invoked until after a test had cleaned up the DOM in preparation for the next test. The timeout function fires and then throws an error because none of the elements it expects are on the page.
Libraries that set a lot of native key handlers can also be problematic. Native browser key events are a pain to mock, and the same mocking techniques are not available in all browsers. If you want to unit test in all browsers, you have to write some truly hideous tests.
The fact is that a lot of browser API's are terrible, and tools like jquery can help smooth over browser differences and make it easier to test your code that relies on a library.
You don't need a whole framework to intermediate events and timers.
One approach is to have a library that you call instead of the native addEventListener, setTimeout, etc. method.
Another is to use something like zones.js which does this for basically ever async API in the browser, and then use test utilities that let you play with time, like flushing all timers, or advancing time by specific amounts.
This is indeed true for V8 IIRC, but even if it weren't, both arguments.length and arguments[x] are safe to use without stopping optimisation, so even a very cautious JavaScript dev can effectively clone the arguments object in a roundabout sort of way and pass it to apply, like so:
// given a context and function 'func'
var args = [];
for(var i = 0; i < arguments.length; i++){
args[i] = arguments[i];
}
func.apply(context, args);
If you want to use those modules in any framework you just wrap it in a tiny thin wrapper and I don't have to keep up with framework changes and all that...
It wont be forever but I think its a fair stepping stone. "set state" I believe is a good alternative to setter/getter and dirty checkers. But react still has some major list issues.
Finding the difference between a list of 10,000 items is absurd. This is where I believe React will find its replacement or forced to adapt to.
I've been advocating this for a while, business logic should not be tied to a specific framework. It can be brought into any framework with a super thin, easy to write/read wrapper.
But UI level stuff, probably should be done in your framework. So Business logic drives state, which your framework translates into DOM.
It's hard (not impossible) to avoid this with some user interface code, due to the amount of connected, stateful cruft (hopefully the state is pushed out to the edges, but it has to be there somewhere).
I do appreciate little utility libraries like Ramdajs and Validatejs, though.
This article was a nice reminder to make the attempt to decouple small plugins, though. I like the idea of the base module + alternate framework adapters.
Just yesterday I spent 4 hours looking for something that'd let me use DOM queries with map and forEach. I settled on NodeList.js. I didn't want JQuery or Zepto or anything larger than 1 KB, but I also didn't want to write something to do this, something I'd have to copy and paste into the next project.
Not sure why you spent 4 hours on that, in addition to the other response there's also this method, which lets you call map on it and also call all the methods available to NodeList (you'd lose access to those if you converted it to an array)
var classNames = [].map.call(nodeList, (entry) => entry.className)
The 'call' function in java script allows you to specify the "this" in the calling of the function. So we grab the map function from an empty array, and call it with nodeList as "this". You can find out more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
You could also use that to make a super simple method to allow you to call map on any array like object, then you can import this into your other files:
function map(arr, callback) {
return Array.prototype.map.call(arr, callback);
}
Then call it like
var classNames = map(nodeList, (node) => node.className);
Well, I spent four hours on it because I was also considering the other fancy features of these libraries and playing with them. Which is why this post made me think of yesterday's four hour fancy javascript helper library exploration session.
forEach is coming to DOMNodeList plus DOMNodeList is an iterable and you should be using for..of to iterate in them. There is also Array.from that others mentioned
I believe anyone who's spent long enough time across multiple programming backgrounds and looked at the JavaScript ecosystem would understand how solid Web Components are. They're objects, and you fill methods on them. But that's not "revolutionary" enough for this community. There's no flash or wow with it, it's just solid programming.
Also, everyone loves to hate the DOM. It's so "low level."
Additionally, I feel Google tainted the idea of them by making Polymer the face of the standard, rather than letting it stand by itself.
> The point in question is that developing a library that specifically targets a framework is a waste of your time, because when you eventually move on to the next framework
( this isJavaScript,youwill) you’ll
kick yourself over tightly coupling the library to the framework.
How about not targeting a specific language either?