DEV Community


Posted on

Design Principles of Crank.js and React

On April 15, 2020, a new frontend JavaScript framework was released. Yes, I know. I can already hear the groans of developers who have had to learn PHP, then Angular, then React, and now worry that they may have to learn this. The author of the framework apologized for the release, but thought that his framework would meaningfully impact the way that user interfaces are made in a way that benefits developers and users. In his article he explains the need for Crank.js and how he thinks React’s features are becoming more and more hacky and arcane. One example of this is React’s somewhat recent hooks API. While many React fans hail the release of hooks as the greatest feature of the framework, the author of Crank.js did not agree, describing his distaste for the “strangeness and pitfalls” of the hooks API. Anyone who has used React’s functional components with hooks will be familiar with how everything must be memoized to avoid unnecessary re-renders, requiring dependencies of functions to also be memoized, and so on and so forth until your every function is wrapped with useCallback and every variable with useMemo. The creator of the new framework also seemed less than enthusiastic about the upcoming release of React’s Suspense API, which is designed to avoid data-fetching waterfalls. Suspense detects that components need to load by having those components throwing promises during render time (yes, with the throw keyword as if throwing an error) which understandably seems weird. He complains that “it doesn’t even matter what the thrown promise fulfills to; instead, it’s an elaborate way to notify React that your components are ready to try and render again.” Furthermore, a cache must be maintained in order to avoid fetching the same data again and again on each re-render. In order to cache async calls, you must be able to uniquely key each one and know when to invalidate its results. The author expresses his disinterest for having to manage cache invalidation, and references the well-known joke that cache invalidation is one of the hardest problems in computer science. He eventually felt alienated by where React was going. He envisioned a world in which asynchrony was managed by JavaScript alone instead of by strange framework hacks. Why can’t components just return promises instead of JSX when they’re not done loading yet?

In Crank.js, they can. Components can be plain functions, async functions, generators, and async generators. When a component needs to wait to render, it can simply return a promise instead of JSX. To add state to a component, you can simply add local variables and make the function a generator that yields JSX infinitely. The library also allows you to dispatch and handle custom events, similar to Vue.js. While the Crank framework makes use of numerous design principles shared by nearly all frontend frameworks, such as lifting state up and using composition over inheritance, it eschews the notion that all renders must be pure and side-effect free, a core principle of React’s design that the author of Crank.js describes as “dogmatic” and impractical.

Not everyone is totally on-board with the principles of this new framework though. One day after the release of Crank.js was announced on Reddit, Dan Abramov, a lead React contributor, came out in defense of React. He explained that React’s component-purity-driven philosophy is only a means to solving a problem of the end user: it’s nice to be able to render components before they appear on screen so that they paint faster. He said that one “can only really do those things safely when rendering is pure and safe to try at any time or interrupt”. Dan also argued that caching is not an ugly side effect of the Suspense API: Suspense was built around the use of a cache because caches improve the user experience by speeding up data fetching. However, he ultimately said that he “totally agrees with [the author of Crank.js] it’s great to have more options that explore various tradeoffs.”

Top comments (0)