Hacker News new | ask | show | jobs
by skydhash 1551 days ago
I'd said that it is a non-issue. One of the main tenets in React is that your component re-renders when the prop changes. I think anyone should research what exactly change in this context means and take care of not triggering the re-render when it's not wanted. The author goes out of the way to use examples that are what I would consider bad code and not something that should ever pass a code review.

I have not dealt with many junior devs, but creating an object either by using a literal or restructuring is still creating a new object and no one should expect it to be the same. Equal perhaps, but not the same. Maybe someone should explain scope and instance lifetime in the JavaScript world instead of blaming React for these. Because there is no surprise that the component re-render when you change the prop.

4 comments

Yea, the examples seem disingenuous to me.

> Most bugs can be solved by moving hooks away from the components and using primitives as the only dependencies.

Or just use primitives in the dependency array of your existing hooks?

Exactly.
The point is that "a prop changed" can't be reasoned within a component. If I receive an object as a prop, I can only reason if that pointer in memory will be the same, not wether it has changed. The allocation can happen up in the component hierarchy making this a subtle for even the most senior developers.
That's why a number of people (myself included) advocate for overzealous/defensive use of useMemo/useCallback as a means of ensuring that a prop changing is always meaningful. The rationale is summarised here: https://www.zhenghao.io/posts/memo-or-not

There are good reasons for _not_ doing this, since using these hooks isn't free; and technically speaking useMemo isn't an identity guarantee (though it currently behaves as one), but I haven't experienced any of the common useEffect pitfalls since adopting this methodology a couple of years ago.

But as my sibling comment points out, a lot of the need for this defensive coding would go away if there were more ways of defining equivalence. I hope that one day the record and tuple proposals land, which should help a bit. But i'd also like to see something like Python's __eq__ and __hash__ in JavaScript too - perhaps done in a similar way to [Symbol.iterator].

Props are outside the boundary of concerns for a component as far as allocation go. You react to value and identity. The parent component is the one concerned with actually giving you the correct props. Very much like a function call would work. You can provide a signature or a contract, but you are not actually expected to deal with every kind of abuses, especially things that fall outside good practices.

Perhaps it is a concern in bigger codebase. AFAIK, there is no method to enforce this kind of contract. But documentation could be a big help. Like documenting how changes to a prop impact the behavior of the component - like the common `initialValue` and `value`.

In an ideal world, you're absolutely right. Reality is different, though—sometimes people make mistakes and generate objects on-the-fly without memoizing them, and sometimes you have to deal with third-party or legacy code that you can't control. Reasoning about the behavior of hooks in these types of situations can become really difficult, and there really is no good answer aside from being ultra-defensive with how you handle props and dependencies (which has many of its own downsides).
in java terms, it sounds like you need to define your own "equals()/hashCode()". The "reference equality vs logical equality" is well-understood in that domain at least.
This was my overall thought as well. When I get myself into the type of trouble he's describing, it's usually because I either designed the effect poorly or it's simply doing too much. I also find that I'm abstracting certain complex effects into Redux Sagas more and more which solves some issues around effects depending on the result of other effects- not all processes should be triggered in this way.

It took me a long time of grinding on various effects scenarios to figure out efficient, easy to understand solutions to complex behaviors. That said, I do agree with his points on under/over subscription... that is still something that frustrates me, especially when the linter wants me to complicate something that seems unnecessary.

Relying on the distinction between reference and value equality here is clearly not a good practice - as a fix, the author recommends only using primitive types as dependencies.