jasonkillian's comments

jasonkillian | 3 years ago | on: A Visual Guide to React's useEffect (2021)

Agreed, but it's a pretty big footgun because `useEffect` is often the seemingly easiest way to do the things you list.

I've had similar conversations many times with coworkers before when they were using `useEffect` to keep a state value in sync with a prop. The officially-recommended alternative of manually storing and updating an extra piece of state containing the previous prop value is cumbersome and also had ways it can go wrong. So, since `useEffect` works well enough in most cases and is easier, often the code just sticks with that method. I'm not entirely sure what's really best all tradeoffs considered, but it definitely illustrates how rough edges often pop up in hook-based React.

jasonkillian | 3 years ago | on: Why React Re-Renders

It is a bit more complicated in practice though than "a React component is just a function that rerenders when called". In some ways, the function acts more like a class, and then React, internally, uses it to create "instances" of components that have their own set of data stored. (Which is why hooks like useState, useRef, etc. can work - because data is being stored internally in React tied to a component instance.)

It _is_ true that when you call a React function component it "runs its code" just like any regular old JS function. But when that function gets run and what all the side effects of its code are actually is quite complex.

jasonkillian | 3 years ago | on: Extreme explorations of TypeScript's type system

> TypeScript's type annotations are really a DSL embedded into JavaScript. And they can, and, depending on the problem at hand, should be treated as such.

I think this is the key. If treated as you describe, meaning the advanced types are well-written, well-documented, and well unit-tested as if they are "true" code, then using them shouldn't be too much of an issue.

However, I think people often just assume that the types aren't "real" code and thus the normal concepts of good software engineering don't apply and type monstrosities which nobody can understand result.

Imagine if this code[0] wasn't well-documented, fairly clearly written, and also tested. It would definitely be a liability in a codebase.

In addition, the rules of how advanced TypeScript concepts work can be quite nuanced and not always extremely well defined, so you can end up in situations where nobody even _really_ understands why some crazy type works.

[0]: https://github.com/sindresorhus/type-fest/blob/2f418dbbb6182...

jasonkillian | 4 years ago | on: Modeling chopping onions: The importance of mise en place

Happened to see this thread on HN, (and disclaimer I'm an engineer at Hex) very cool project jwithington!

Wanted to take a second to address your comment about performance, lqet. All your points are absolutely fair - I personally apologize for some of those layers of divs and some of that JS as they're definitely my fault!

For context, Hex lets you write Python and SQL code in a notebook-esque format and then create apps from that to share across the web. So there's actually quite a breadth of functionality we need to support under the hood that adds to frontend complexity. We also revamped our whole app-building experience recently, so there's a couple straggling bugs (like the text selection one you mentioned, whoops!).

But I totally agree with you - thinking about all that JS makes me wince a little haha as we definitely care and want to improve frontend performance. We plan to make better use of code-splitting and lazy-loading of JS so that the frontend code for more complex apps is only pulled in if/when necessary. (We also want to work on building better tooling to make analysis of code-splitting effectiveness easier - we've found that a lot of existing webpack bundle analyzer tools don't provide enough visibility for our use cases. Maybe an open source project for us one day!) And we want to decrease over-the-wire data size and reduce necessary network calls so you can get a faster initial load. We're a small team, so can't make promises when exactly this will all happen, but hopefully with these changes and other improvements things will feel a bit snappier someday soon

jasonkillian | 4 years ago | on: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

In addition to being essentially a combined "filter" and "map", it's also a "better" filter than filter itself in TypeScript in such that it narrows types much more ergonomically[0].

In TypeScript, you might have an array of multiple types (e.g. `Array<A | B>`), and use a `filter` call to only keep the `A`s. However, in many situations TypeScript can't figure this out and the resulting array type is still `Array<A | B>`. However, when you just use `flatMap` to do nothing more than filtering in the same way, TypeScript can determine that the resulting type is just `Array<A>`. It's a bit unfortunate really - `filter` is faster and more readable, but the ergonomics of `flatMap` type-wise are so much nicer! Just some interesting trivia.

[0]: https://github.com/microsoft/TypeScript/issues/16069#issueco...

jasonkillian | 4 years ago | on: Flatten Arrays in Vanilla JavaScript with Flat() and FlatMap()

Wouldn't recommend doing this - if the original array is of significant length this'll get quite slow because `acc.concat` has to create a brand new array of slightly longer length on each iteration it's called. Better to just use `push` like you suggested before and then return the array if you want to use `reduce`.

jasonkillian | 4 years ago | on: We memo all the things (2020)

I don't disagree with you on readability being important or on the value of developer time. It's just that the marginal costs of `memo`, `useMemo`, and `useCallback` are quite low. They don't add cyclomatic complexity, they don't increase coupling, they can be added to code essentially mechanically and don't carry a large cognitive overhead to figure out how to use, etc.

The main downsides are that they take slightly longer to type and slightly decrease the succinctness of the code. And then there are a few React-specific complexities they add (maintaining the deps arrays and being sure not to use them conditionally) but these should be checked by lint rules to relieve developer cognitive load.

Of course I'd rather not have these downsides, but in the end, it's still much less developer overhead than having to constantly profile a large application to try and figure out the trouble spots and correctly test and fix them post-hoc. And it means users are much more likely to get an application that feels snappier, doesn't drain as much battery, and just provides a more pleasant experience overall, which is worth it imo.

jasonkillian | 4 years ago | on: We memo all the things (2020)

This is a great article and I agree with it fully.

The argument that a lot of popular React voices have made, "React is fast and it's prematurely optimizing to worry about memoizing things until a profile shows you need it", has never rung true with me. First and foremost, there's a huge time cost to figuring out what those exact spots that need optimization are, and there's also an educational cost with teaching less experienced engineers how to correctly identify and reason about those locations.

There are only two reasonable arguments for not using `memo`, `useMemo`, and `useCallback`. The first is that it decreases devx and makes the code less readable. This one is true, but it's a very small cost to pay and clearly not the most important thing at stake as it's only a slight net effect. The second argument is that the runtime cost of using these constructs is too high. As far as I can tell, nobody has ever done a profile showing that the runtime cost is significant at all, and the burden of proof lies with those claiming the runtime overhead is significant because it doesn't appear that it is typically when profiling an app.

So, given that the two possible reasons for avoiding `memo`, `useMemo`, and `useCallback` are not convincing, and the possible downsides for not using them are fairly large, I find it best to recommend to engineering teams to just use them consistently everywhere by default.

jasonkillian | 5 years ago | on: Dates and Times in JavaScript – A New API for Dates from TC39

It's a current WIP idea for the spec: https://github.com/tc39/proposal-temporal/pull/700

Figuring out a name is still part of the ongoing discussion, so this specific case of `LocalDateTime` isn't a huge deal, and I might have misrepresented things slightly in my original comment, sorry! But I do think the overall point still stands - that it might be best to just use the same names and terminology as Java does.

jasonkillian | 5 years ago | on: Dates and Times in JavaScript – A New API for Dates from TC39

I think this is a great proposal and a huge step in the right direction for JS. I am curious though, is there a reason not to just essentially duplicate the Joda[0]/Java[1]/ThreeTen[2] API? As far as I understand, they are generally considered a gold standard as far as datetime APIs.

Is it too Java-y that it wouldn't make sense to port to JS? Are there copyright implications?

The JS Temporal proposal _does_ as far as I can tell, share many of the underlying fundamental concepts, which is great, but then confusingly has some types, such as `LocalDateTime`, which mean the exact opposite of what they do in the well-known Java API [3].

There is still discussion going on about these details, but from my perspective it seems like the best thing would be to just copy the Java naming conventions exactly.

[0]: https://www.joda.org/joda-time/

[1]: https://docs.oracle.com/javase/8/docs/api/java/time/package-...

[2]: https://www.threeten.org/

[3]: https://github.com/tc39/proposal-temporal/issues/707

jasonkillian | 5 years ago | on: A Critique of React Hooks

Hooks are great for simple use cases like `useState`, `useContext`, some uses of `useRef`, etc. and can make the code easier to read and reason about (while conveniently working very well with TypeScript).

The rules do start to get really tricky though with complex use cases of `useEffect` and multiple levels of nested hooks, and implementation problems are often not easy to spot or debug.

Dan Abramov has written a lot about the philosophy of hooks[0] at his site overreacted[1], I'd love to see a 'retrospective' write-up from him or another React team member about what they think the success and failures of hooks have been so far and if there are any changes planned for the future!

[0]: https://overreacted.io/why-isnt-x-a-hook/, https://overreacted.io/algebraic-effects-for-the-rest-of-us/, https://overreacted.io/a-complete-guide-to-useeffect/

[1]: https://overreacted.io/

jasonkillian | 7 years ago | on: Show HN: We Got Sick of Giving Out 'Ballpark Estimates' So We Built This

Weirdly enough, both websites use this exact same quote: "This means you'll need to make an API (application programming interface). It's how all your friendly apps talk to each other."

Either plagiarism was involved, or there's some underlying library that's the same for both, or they both hired the same company to build their MVP ;)

HowMuchToMakeAnApp is by "Commite, a digital studio specialized in driving ideas from inception to launch" based out of "Seville, San Francisco & New York".

BuildMyMVP is made by "ProductDone a digital studio based in Auckland, New Zealand".

The two sites side by side: https://imgur.com/a/e7OVYVa

jasonkillian | 7 years ago | on: 38% of bugs at Airbnb could have been prevented by using types

> Surely Node.js is going to be looked back upon as one of the worst mistakes in the history of 21st century programming.

Why is that? There were dynamically-typed backend languages long before Node.js became popular. And you now can easily have a TypeScript-based Node.js backend, which gives you the nice benefit of having the exact same language and types for both your backend and frontend. I'm not saying the Node runtime is perfect, but I don't understand why having a runtime that lets you use JS/TS as a backend or local language is quite such a bad thing.

jasonkillian | 7 years ago | on: Making SetInterval Declarative with React Hooks

The difference is subtle (and took me a bit to figure out!). In the article's example component, the component's "main" function gets re-run on every render and a new `count` variable is created each time. The effect function is only run once at the very first render, and only captures the value from this first render.

I'm having a little trouble explaining the difference well, I think this jsfiddle illustrates the incorrect case the article is talking about representatively: https://jsfiddle.net/7fLnvz5c/

jasonkillian | 7 years ago | on: Making SetInterval Declarative with React Hooks

Wow, I really don't know what to think of this article. I have to give Dan credit that every time I thought the code started looking really crazy, he'd say something like: "Admittedly, [this] code can be disorienting. It’s mind-bending to mix opposite paradigms." And I think it's a good sign he recognizes the fair objections people will bring up.

In addition to the paradigm-mixing the article illustrates, I wonder if the actual hook APIs make things more confusing than necessary at some points. For example, in the `useEffect` hook API:

* the first argument is a function that on every render

* the return value of the first argument is a "cleanup" function which runs when the component is unmounted or before the effect is run again

* the third argument is an array of parameters which controls when the effect is run. If passed an empty array, the effect is only run at mount and cleaned up at unmount.

All the above means that the API is very implicit and doesn't self-document its intent all that well. An example, from the article:

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }

    let id = setInterval(tick, 1000);
    return () => clearInterval(id);
  }, []);
I'm sure with more use these hook patterns will become more familiar. But it's clearly a lot harder to learn and understand than the original simplicity that helped propel React to popularity.

Dan's arguments that hooks provide for easier abstraction and more composability is interesting to me though - if hooks are going to be "worth it" in the long run, I suspect this is why.

jasonkillian | 7 years ago | on: TypeScript Tricks: Type Guards

Type guards are a useful construct, especially when dealing with data from an API that could come in a variety of shapes.

However, if you have control over the data shape, in general, it's nicer to take advantage of discriminated unions as you'll get more automatic typesafe compiler support. If you're not familiar with the construct in TypeScript, the docs give a nice example of them[0].

[0]: https://www.typescriptlang.org/docs/handbook/advanced-types....

page 1