News for hackers. Stuff that matters.
Re:React is becoming a black box by cnity on Thursday September 03, 2020 @09:33AM (#24363858)
So easy I can explain it in a sentence: React hooks must always be called in the same order, and the same amount.

If you push me by asking "why?", I can expand that sentence by including "because each hook call is associated with an index under the hood".

This is probably the only React hook nuance you really have to understand, and it's not particularly difficult.

Re:React is becoming a black box by ricardobeat on Thursday September 03, 2020 @09:43AM (#24363950)
> This is probably the only React hook nuance you really have to understand

Absolutely not. There is the dependencies array, how it only does a strict equals check (no shallow comparison), how you need to memoize other dependencies so they don't change on every render, why it is a problem to leave it empty, why useEffect cannot strictly replace componentDidMount, why you can't put one hook inside the other, how (and which) hooks can trigger a re-render or block updates, how state is captured by the closure and what to do if you need current state, how to persist things across renders using useRef.. and probably more.

Re:React is becoming a black box by svachalek on Thursday September 03, 2020 @10:07AM (#24364174)
Most of these are also true pre-hooks, it just wasn’t as clear that they were actual concerns and most users ignored them, leading to horribly slow but still functional code. Hooks drive you to more correct usage but the consequences of misuse become more severe.
Re:React is becoming a black box by SirHound on Thursday September 03, 2020 @12:25PM (#24365658)
> There is the dependencies array, how it only does a strict equals check (no shallow comparison), how you need to memoize other dependencies so they don't change on every render

So basic memoisation?

> why you can't put one hook inside the other

Hooks inside other functions may be called at any time, how is React to know which component is calling them?

Re:React is becoming a black box by ricardobeat on Thursday September 03, 2020 @06:17PM (#24369521)
> So basic memoisation?

No. Before you just had to worry about shouldComponentUpdate and data received from the 'outside'. With hooks you need to memoize arguments to every function you call within the component - you didn't have to memoize class methods. The concerns and patterns involved are very different.

Re:React is becoming a black box by cnity on Thursday September 03, 2020 @09:45AM (#24363965)
I personally find these quite easy to comprehend, but I suppose I can see how it might be difficult.

Edit: That was snarky, I apologise.

Re:React is becoming a black box by sydd on Thursday September 03, 2020 @11:04AM (#24364712)
Something that I still don't grasp with React: 1. Your render function runs quite often, e.g. on each state change.

2. But it runs a bit differently sometimes: const [a,setA] = useState(1234); Here "a" will have the value 1234 on the first run (what is a first run? When react instantiates this component? When it mounts?) Then this same line of code will assign a different value to this same variable, because setA was called in the past. Why? How does it know that this exact local variable needs a different value?

Re:React is becoming a black box by etblg on Thursday September 03, 2020 @12:33PM (#24365757)
Short, potentially inaccurate:

Render functions are only called when a component is being rendered. React keeps track of which component is currently being rendered. When you call `useState`, React knows which component is currently being rendered and keeps track behind the scenes which state that component has.

The way it knows which value it should return for `a` ties directly in with why you need to call `useState` calls in the same order -- React basically keeps a list of the state behind the scenes, and returns the (secret behind the scenes) state in order. If you change around the order of `useState` calls, it can't use the ordering of the calls to determine which state needs to be returned.

Re:React is becoming a black box by sydd on Thursday September 03, 2020 @04:23PM (#24368468)
and what happens if I unmount and remount it? something like:

<div> {isLoggedIn ? <LogoutButton /> : abcd } </div>

and I change the value of isLoggedIn. Does the state reset? I'm not asking for a solution, just pointing out that even a 'simple' hook like useState has its small quirks.

Re:React is becoming a black box by acemarke on Thursday September 03, 2020 @04:37PM (#24368603)
It does reset, because toggling that flag will unmount the old instance of the component, and then mount a new instance.

This is the same behavior as has always existed with class components and `this.setState()`. Component-scoped state only exists as long as that specific instance of the component is mounted.

Re:React is becoming a black box by 7786655 on Thursday September 03, 2020 @12:27PM (#24365682)
1. When React mounts your component, it creates an object to contain its internal state, including the state of all hooks.

2. Before calling your render function, it stores a reference to that object in a private, global variable. (Global in the sense that there's only one per JS VM) (Javascript is single-threaded so this is perfectly safe to do).

3. Each of the built-in hooks has access to that variable, and use it to increment a number representing the current hook index and then store information in an array at the current index.

4. The next time your component is rendered, that information is still there and the hooks can retrieve it.

5. Also some hooks like useEffect set up functions to be called by react later.

Re:React is becoming a black box by rashkov on Thursday September 03, 2020 @03:56PM (#24368143)
This would make for an interesting exercise.

  const React = /* Implement this */
  
  function HelloComponent(){
    const [myNumber, setMyNumber] = React.useState(0);
    // Simulate rendering data to a screen
    console.log("My number:", myNumber);
    // Simulate rendering a clickable button
    const click = ()=> setMyNumber((num)=> num + 1);
    return click; 
  }
  
  React.mount(HelloComponent); // prints 0
  React.simulateClick(HelloComponent); // prints 1
  React.simulateClick(HelloComponent); // prints 2
  React.simulateClick(HelloComponent); // prints 3
General idea being that the React item may be stateful, but the HelloComponent must be a stateless pure function. How can it draw on React's statefulness, while only using that useState() call?

I am working through this exercise now, but I'm already finding myself doing some pretty hacky things to make this work (like finding out who called a function using arguments.callee.caller.name). Still, it's pretty interesting.

Edit: My solution to this: https://gist.github.com/rashkov/f765917d09ebd629f385c21195f4...

Re:React is becoming a black box by AgentME on Thursday September 03, 2020 @06:50PM (#24369744)
Checking the function's caller breaks the composability of hooks: it stops hooks from being called by other hooks. (Also, it doesn't work in strict mode, which is enforced when you use ES modules.) React is the only thing that calls component functions, so React can just remember what component is being called before it calls it. I've posted a reply on your gist with an example that addresses this.
Re:React is becoming a black box by rashkov on Thursday September 03, 2020 @07:11PM (#24369887)
Very cool! appreciate the response, very interesting to see an improved solution on this.

I was mainly interested in solving for how React lines up the useState calls with the actual data that it's keeping under the hood. So I definitely cheesed through the parts touching on how React keeps track of the component tree. I'm enjoying reading through your interpretation of this though. Thanks

Re:React is becoming a black box by ricardobeat on Thursday September 03, 2020 @09:39AM (#24363913)
Which means you can call it inside a for loop if you have guarantees that the order will always be the same. These are the caveats that don't fit in one paragraph. Also, repeating the rule 'it has to be called in the same order' is not explaining how it works.
Re:React is becoming a black box by cnity on Thursday September 03, 2020 @09:43AM (#24363949)
What makes the fact that each hook call needs to be associated with an index so unacceptable?
Re:React is becoming a black box by ricardobeat on Thursday September 03, 2020 @09:47AM (#24363980)
You could say each call corresponds to a timestamp and they need to be ordered, and the person receiving that information would be none the wiser. It just doesn't lead you to understand what's happening underneath. Some may be fine with stopping at that level of understanding, but the stack-based approach of hooks is quite unnatural for JS and I don't think it is good in the long-term for developers to just 'accept' that as supernatural.
Re:React is becoming a black box by cnity on Thursday September 03, 2020 @09:51AM (#24364013)
So in order to understand not only the basics of the rule, but also the underlying implementation and exactly in what way it would crash, it may require more than a paragraph. I'll concede that. But this is common in programming. As an example, you'd probably spend some time understanding exactly why it is you can't free a pointer twice in C.

There's nothing stopping someone from reading the explanation in the React docs[0] if they want to understand.

[0] https://reactjs.org/docs/hooks-rules.html#explanation