3

Say you have something like this:

const MyComponent = (props) => {
  const refA = React.useRef()
  const refB = React.useRef()
  const refC = React.useRef()
  const [x, setX] = React.useState(true)
  const [y, setY] = React.useState([])

  return <div>...</div>
}

In pseudocode, what is this actually doing when it is called multiple times? Are they doing this basically?

  • Know that MyComponent is about to be converted from the function into a virtual tree.
  • Set a global variable that's going to track when every useX hook is called within a single procedural frame.
  • Store for this component instance the outputs from each of these hook calls.
  • The next time the hooks are called for this component ID, it gets the map of the last returned results from the last useX calls from within this component.

I ask because (a) it seems like it can only be done with a global variable trick of some sort, and (b), the corresponding code is quite complicated and difficult to parse.

Wondering if one could just give a quick high-level overview or some pseudocode on how React implements these magical hooks.

It seems like it's something like this, though more advanced:

let dispatcher = {}

function MyComponent() {

}

function begin() {
  dispatcher.component = {
    refs: [],
    states: []
  }
}

function useRef() {
  let ref = {}
  dispatcher.component.refs.push(ref)
  return ref
}

function useState(val) {
  let state = val
  dispatcher.component.states.push(val)
  return state
}

function end() {
  dispatcher.component = null
}

I just don't see how it can memoize and such with this magic.

This is not the same as the question as knowing how hooks know which component they are for. That is just one aspect to my question, I am asking generally how the hooks work.

alien
  • 749
  • 2
  • 6
  • 16
  • Internally I think it uses reducer to track the state of your component and it returns you a method which you can call to make an update to the state. It is global in theory but still scoped within React's framework. I suggest you to go through React's Smoosh mode video which highlights tons of great details of how internal works, its pretty advanced but very pleasing once you understand it: https://www.youtube.com/watch?v=aS41Y_eyNrU – Rikin Aug 09 '19 at 15:28
  • Are you asking (a) how hooks implemented or (b) what happens within react on each call? – Dennis Vash Aug 09 '19 at 15:28
  • Both, how the hooks actually work, which entails what happens when it is called. I don't need to know about the rendering part, mainly how the hooks work. – alien Aug 09 '19 at 15:37
  • A very high-level description is in the docs: https://reactjs.org/docs/hooks-faq.html#how-does-react-associate-hook-calls-with-components – Ryan Cogswell Aug 09 '19 at 16:05

1 Answers1

2

The question about how hooks implemented is pretty broad to explain each hook, let's take for an example useRef and useState.

While useRef(initialValue) is just an object with a current key, possible implemention:

const myRef = useRef('myReference');
const useRef = (initialValue) => ({ current: initialValue });

Hooks are just arrays, this array referred to state - and it is not shared with other components but it is maintained in a scope that is accessible to the subsequent rendering of the specific component.

On each setter function call, we can use a cursor to define which state needs to be used.

Possible implementation of useState:

const state = [];
const setters = [];
let firstRun = true;
let cursor = 0;

function createSetter(cursor) {
  return function setterWithCursor(newVal) {
    state[cursor] = newVal;
  };
}

// This is the pseudocode for the useState helper
export function useState(initVal) {
  if (firstRun) {
    state.push(initVal);
    setters.push(createSetter(cursor));
    firstRun = false;
  }

  const setter = setters[cursor];
  const value = state[cursor];

  cursor++;
  return [value, setter];
}

// Our component code that uses hooks
function RenderFunctionComponent() {
  const [firstName, setFirstName] = useState("Rudi"); // cursor: 0
  const [lastName, setLastName] = useState("Yardley"); // cursor: 1

  return (
    <div>
      <Button onClick={() => setFirstName("Richard")}>Richard</Button>
      <Button onClick={() => setFirstName("Fred")}>Fred</Button>
    </div>
  );
}

// This is sort of simulating Reacts rendering cycle
function MyComponent() {
  cursor = 0; // resetting the cursor
  return <RenderFunctionComponent />; // render
}

console.log(state); // Pre-render: []
MyComponent();
console.log(state); // First-render: ['Rudi', 'Yardley']
MyComponent();
console.log(state); // Subsequent-render: ['Rudi', 'Yardley']

// click the 'Fred' button

console.log(state); // After-click: ['Fred', 'Yardley']
Dennis Vash
  • 50,196
  • 9
  • 100
  • 118