3

Can anyone help me understand how functional cmp gets re-render whenever there is change in state.

refer below e.g.

function App() {
  const [count, updateCount] = useState(0);

  return (
    <div className="App">
      <h1>{count}</h1>
      <button onClick={() => updateCount(count + 1)}>Update</button>
    </div>
  );
}

If I compare this with class based react component then over there we had render function inside class which I believe must be getting trigger whenever there is changes in state or props (there must be chain of life-cycle functions and one of them would be this.render())

But with above code structure App is a functional component and inside App we are changing state/count, how react get to know that after updateCount/setter function, App/(or its parent function) should get called again? I mean we are not passing reference of App function to useState right.

Manish
  • 31
  • 2
  • It hooks to internal react state. Like there is a hidden class behind every functional comonent. Check the FAQ https://reactjs.org/docs/hooks-faq.html#how-does-react-associate-hook-calls-with-components – UjinT34 Mar 20 '19 at 14:17
  • Related question: https://stackoverflow.com/questions/53974865/how-do-react-hooks-determine-the-component-that-they-are-for/53980190#53980190 – Ryan Cogswell Mar 20 '19 at 15:54

2 Answers2

0

Classes

Before going into how hooks differ, let's look how classes process state updates.

When you call the base class setState method, it enqueues a state change.

this.updater.enqueueSetState(this, partialState, callback, 'setState');

The updater object it uses is defined here. Let's look at the implementation of enqueueSetState.

First it gets a fiber using the getInstance function, which is an alias of a broadly named get function.

const fiber = getInstance(inst);

It's a bit obscurely named, but all it does is take an object instance (your class component), and return its _reactInternals property value.

It creates an update object where payload is the argument that was passed to setState. It then schedules this update object, without executing it, and marks the component needs to be re-rendered using scheduleUpdateOnFiber.

const update = createUpdate(eventTime, lane);
update.payload = payload;
if (callback !== undefined && callback !== null) {
  if (__DEV__) {
    warnOnInvalidCallback(callback, 'setState');
  }
  update.callback = callback;
}

const root = enqueueUpdate(fiber, update, lane);
if (root !== null) {
  scheduleUpdateOnFiber(root, fiber, lane, eventTime);
  entangleTransitions(root, fiber, lane);
}

The queue of previously scheduled updates is processed as the component is updated (the render triggered by setState starting with our element as the root).

It calls updateClassInstance that both executes the class's lifecycle methods, and returns whether the component needs to be re-rendered.

shouldUpdate = updateClassInstance(
  current,
  workInProgress,
  Component,
  nextProps,
  renderLanes,
);

https://github.com/facebook/react/blob/a8c9cb18b7e5d9eb3817272a1260f9f6b79815a2/packages/react-reconciler/src/ReactFiberClassComponent.new.js#L1142

processUpdateQueue(workInProgress, newProps, instance, renderLanes);

If the state has changed, it will proceed to render the component.

Hooks

Contrary to a class component, which manages its own state internally, a function component relies on calling hooks to have React store the state "for you".

Every call to useState (or any other hook that internally depends on storing a value) will add this piece of state to a per component linked list.

React relies solely on the order of this list to retrieve the right values for each hook on subsequent render passes. That's why you can only use hooks at the top level, and never in loops / conditionals. Otherwise React has no way of knowing which data belongs to which hook call.

React hooks each have 2 implementations , which are switched internally. One for when the component mounts, and one for updates. This makes sense as the logic for both cases is quite different.

On the first render, it uses mountState.

function mountState<S>(
  initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
  const hook = mountWorkInProgressHook();
  // ...

mountWorkInProgressHook is where the hook call's entry in the linked list is created.

if (workInProgressHook === null) {
  // This is the first hook in the list
  currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
} else {
  // Append to the end of the list
  workInProgressHook = workInProgressHook.next = hook;   
}

Next mountState initializes the state according to the argument of useState.

if (typeof initialState === 'function') {
  // $FlowFixMe: Flow doesn't like mixed types
  initialState = initialState();
}
hook.memoizedState = hook.baseState = initialState;

It also creates an update queue, similar to the internal update queue of class components.

  const queue: UpdateQueue<S, BasicStateAction<S>> = {
    pending: null,
    lanes: NoLanes,
    dispatch: null,
    lastRenderedReducer: basicStateReducer,
    lastRenderedState: (initialState: any),
  };
  hook.queue = queue;

The setState function you get back will put the arguments in the queue whenever you call it.

On update renders, useState uses updateState, which internally calls updateReducer. It processes all updates in the queue.

Finally, to trigger a render when you call setState, after adding the update to the queue, it schedules the element as a root that needs to be re-rendered. Source

const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
if (root !== null) {
  const eventTime = requestEventTime();
  scheduleUpdateOnFiber(root, fiber, lane, eventTime);
  entangleTransitionUpdate(root, queue, lane);
}

Comparison

For the most part classes and functional components end up doing the same thing here.

  • Add updates to a queue
  • Mark the element as needing render
  • Apply queued state updates as the component is rendered, before logic that uses the state

However they use a separate implementation for each of these things, leading to subtle differences.

When exactly is state updated?

With class components, you state is always going to get updated before it even calls the class's render method.

For function components with hooks, the state updates happen as each hook is called.

While it's clearly a different approach, this difference doesn't have much practical implications. State updates happen in time in both cases.

inwerpsel
  • 2,677
  • 1
  • 14
  • 21
-1

Your code inside your render function REACTS to your state. People like to say that UI is a visual representation of your component's state.

Until Hooks release, there was no way to have React's internal state in function component. You could've been using Redux, MobX or some other third party lib to manage your state, but no way to connect to component's local state.

Your render function behaves exactly like it would in stateful class component,.

  • 1
    Question is about how react is re-rendering/invoking the functional component based on the hook which is inside of the same functional component – kgsnaidu May 21 '21 at 16:07