Functional Components and Hooks
Understanding implementation of functional components and hooks in a custom reconciler.
Introducing Functional Components
In React, components can be defined as functions that return elements. These are called functional components.
Function components are differents in two ways:
- The fiber from a function component doesn’t have a DOM node
- The children come from running the function instead of getting them directly from the props
To update Functional Components, we add a new updateFunctionComponent function that will call the component function to get the children.
function updateFunctionComponent(fiber) {
const children = [fiber.type(fiber.props)];
reconcileChildren(fiber, children);
}Implementing Hooks
To implement hooks, we need to keep track of the currently rendering fiber and the hook index. We can do this by adding two variables: wipFiber and hookIndex.
let wipFiber = null;
let hookIndex = null;
function updateFunctionComponent(fiber) {
wipFiber = fiber;
hookIndex = 0;
wipFiber.hooks = [];
const children = [fiber.type(fiber.props)];
reconcileChildren(fiber, children);
}Now we can implement the useState hook. This hook will use the wipFiber and hookIndex to store and retrieve state.
function useState(initial) {
const oldHook =
wipFiber.alternate &&
wipFiber.alternate.hooks &&
wipFiber.alternate.hooks[hookIndex];
const hook = {
state: oldHook ? oldHook.state : initial,
};
wipFiber.hooks.push(hook);
hookIndex++;
return [hook.state];
}useState should also return a function to update the state, so we define a setState function that receives an action and updates the state accordingly.
So, the complete useState implementation looks like this:
function useState(initial) {
const oldHook =
wipFiber.alternate &&
wipFiber.alternate.hooks &&
wipFiber.alternate.hooks[hookIndex];
const hook = {
state: oldHook ? oldHook.state : initial,
queue: [],
};
const actions = oldHook ? oldHook.queue : [];
actions.forEach((action) => {
hook.state = action(hook.state);
});
const setState = (action) => {
hook.queue.push(action);
wipRoot = {
dom: currentRoot.dom,
props: currentRoot.props,
alternate: currentRoot,
};
nextUnitOfWork = wipRoot;
deletions = [];
};
wipFiber.hooks.push(hook);
hookIndex++;
return [hook.state, setState];
}Polishing
With this, we have a basic implementation of functional components and the useState hook in our reconciler. We can now create components as functions and manage state within them using hooks.
Next we will see how can we use this custom reconciler in real life and play around with it.