Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does JavaScript mechanism behind react hooks work?

My question relates to Javascript mechanisms that make react hooks possible.

Recent development in React allows us to create hooks, ie. for React state, within as simple function like:

function App () {
  const [someVar, setSomeVar] = useState('someVarDefaultValue');
  return (
    <div 
      onClick={() => setSomeVar('newValue')}>{someVar}
    </div>
  );
}

The hook useState returns an array with an accessor and a mutator, and we use them by array decomposition inside our App function.

So under the hood, the hook looks something like (just a pseudocode):

function useState(defaultValue) {
  let value = defaultValue;

  function setValue(val) {
    value = val;
  }

  return [value, setValue];
}

When you try this approach in JS it won't work - value decomposed from array will not update if you use setValue somewhere. Even if you use the value as an object, not a primitive defaultValue.

My question is how does hook mechanism work in JS?

From what I've seen in React sourcecode it uses reducer function and type-checking with Flow. The code is tricky to follow for me to understand the big picture.

This question is not about how to write custom hooks in React.

It's also not question how hooks work under the hood in context of React state management answered in this question: React Hooks - What's happening under the hood?

like image 387
mtx Avatar asked Dec 22 '18 11:12

mtx


People also ask

How do React Hooks actually work?

Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don't work inside classes — they let you use React without classes. (We don't recommend rewriting your existing components overnight but you can start using Hooks in the new ones if you'd like.)

How do React Hooks really work under the hood?

Under the hood, React knows when the function is invoked the first time in order to mount the component. During that first invocation, React sets up all the declared state variables — using the initial state values that have been specified and then provides a mechanism to keep track of their changes.

What are JavaScript Hooks?

Hooks are JavaScript functions that manage the state's behaviour and side effects by isolating them from a component. So, we can now isolate all the stateful logic in hooks and use (compose them, as hooks are functions, too) into the components.

How React Hooks are created?

Hooks are the new feature introduced in the React 16.8 version. It allows you to use state and other React features without writing a class. Hooks are the functions which "hook into" React state and lifecycle features from function components.

What are react hooks?

Hooks don’t replace your knowledge of React concepts. Instead, Hooks provide a more direct API to the React concepts you already know: props, state, context, refs, and lifecycle. As we will show later, Hooks also offer a new powerful way to combine them.

How does myreact work with hooks?

Like React, it keeps track of component state (in our example, it only tracks one component, with a state in _val ). This design allows MyReact to “render” your function component, which allows it to assign the internal _val value every time with the correct closure: Now this looks a lot more like React with Hooks!

What are react effects and how do they work?

Effects are declared inside the component so they have access to its props and state. By default, React runs the effects after every render — including the first render. (We’ll talk more about how this compares to class lifecycles in Using the Effect Hook .)

Can I reuse stateful behavior between React components?

(We don’t recommend rewriting your existing components overnight but you can start using Hooks in the new ones if you’d like.) React provides a few built-in Hooks like useState. You can also create your own Hooks to reuse stateful behavior between different components.


1 Answers

The state value has to be stored outside of the useState function, in some internal representation of the component instance, so that it returns persistent results across calls. Additionally setting the value has to cause a rerender on the component it gets called in:

// useState must have a reference to the component it was called in:
let context;

function useState(defaultValue) {
  // Calling useState outside of a component won't work as it needs the context:
  if (!context) {
    throw new Error("Can only be called inside render");
  }

  // Only initialize the context if it wasn't rendered yet (otherwise it would re set the value on a rerender)
  if (!context.value) {
    context.value = defaultValue;
  }

  // Memoize the context to be accessed in setValue
  let memoizedContext = context;

  function setValue(val) {
    memoizedContext.value = val;

    // Rerender, so that calling useState will return the new value
    internalRender(memoizedContext);
  }

  return [context.value, setValue];
}

// A very simplified React mounting logic:
function internalRender(component) {
  context = component;
  component.render();
  context = null;
}

// A very simplified component
var component = {
  render() {
    const [value, update] = useState("it");
    console.log(value);
    setTimeout(update, 1000, "works!");
  }
};

internalRender(component);

Then when setValue gets called, the component rerenders, useState will get called again, and the new value will get returned.

The upper example is very simplified. Here's a few things that React does differently:

  1. The state is not stored in a "context property" but rather in a linked list. Whenever useState is called, the linked list advances to the next node. That's why you should not use hooks in branches/loops.
  2. The setState function gets cached and the same reference gets returned each time.
  3. Rerendering does not happen synchronously.
like image 141
Jonas Wilms Avatar answered Oct 17 '22 17:10

Jonas Wilms