React v16.8.0 has been released, which means that React Hooks are now available in a stable release of React. This is something I’ve been looking forward to, ever since I first started playing around with hooks in the alpha releases. I believe hooks are going to bring about a sea change in the way React components are written, and composed, and this is a change for the better. If you have not yet heard of React hooks, you should probably go read , on the React documentation site. Following that you might also want to read this excellent by Dan Abramov. this overview on hooks article on hooks Beware: Here there be Magic! I’ve been talking about hooks a lot recently, and a lot of times, somebody points out that while hooks seem to be a great solution to the problem of composing side-effects, and state management in our React components, it also appears to be a bit of a black box, with unidentifiable magic happening to get it to actually work. Most of these people justified their claims of hooks being magic due to: Hooks requiring to be called in the same static order during every render. Hooks can only be called from within other hooks or function components. The hook appears to track the of the component, which was never passed to it. useState state While magic might be fun if you’re at a show, it certainly is not fun when you’re writing software, and for the life of you, cannot figure out why your library is doing something weird, unexpected, and defying all logic. So let’s deconstruct the magic, and see what exactly is happening behind the scenes in each of these cases. Let the UnMagicking Begin! Hooks require to be called in the same static order during every render. The first factor that people notice is that (as mentioned in the ), hooks are very fussy about their call order. Specifically, React mandates that on every render, the order in which the hooks are called must be the same. This means that you can not call hooks inside loops or conditions, and instead they have to be called at the top-most level. Rules for Hooks The reason behind this is quite simple. Consider a few hooks defined in a function component like this: const [firstName, setFirstName] = useState('John'); const [lastName, setLastName] = useState('Doe');const [age, setAge] = useState(28); As you can see, this is a plain ES6 call to a function called . The only information that the function ever gets is the initial value that is passed into it (well not exactly, but we'll get back to that later). The call then returns an array in the form of . useState useState setState [value, setter] On subsequent renders, the same method will return the updated state value for that particular property. In order to do this, the method will have to somehow keep track of each property requested. However, there is absolutely no place where we are passing a of any sort to the method. useState useState key useState So how exactly does know what state to return for which call? Well uses the call sequence number inside the render function to track the state. So in the above example the first call to will always return the value of and it's setter, the second call will always return the value of and it's setter and so on and so forth. useState useState useState firstName lastName If like me, you are probably thinking that there would have to have been a much better way to implement this, instead of relying on call order, which seems quite hacky. Sebastian Markbage left an on the React Hooks RFC, addressing that amongst other things. You should probably go read that too. excellent comment Anyway, the short of it is that if you’re using hooks, you absolutely must call the hooks in the same order on every render, or Bad Things Will Happen, with React thinking you want to get or set a certain state value, when the one you want is entirely different. An easy way to understand this is to think of as maintaining a key-value map linking the call order to the state value. On the first render, initializes the key-value map as: useState useState const [firstName, setFirstName] = useState('John'); // internal state: {0: 'John'}const [lastName, setLastName] = useState('Doe'); // internal state: {0: 'John', 1: 'Doe'}const [age, setAge] = useState(28); // internal state: {0: 'John', 1: 'Doe', 2: 28} On the second render, already has a key-value map, so instead of setting values on it, it just retreives the values based on the call order. Please note, does not actually maintain a simple key-value map, and the actual implementation is a bit more complicated than that. But for the purpose of understanding why the call order matters, you can think of it as effectively being a key-value map. useState useState Hooks can only be called from within other hooks or function components. Another big rule of hooks, is that you can only ever call a hook from a React function component, or from within other hooks. If you think about it, this makes complete sense — if your hook is going to have to get or set the state of a component, or trigger side-effects, either directly or indirectly, then it needs to be linked to a specific React component, to which that state would belong. Otherwise, how would React know which component’s state your hook is trying to access? So far all seems good. There’s nothing really magicky about this one is there? Well if you want to see the magic, you need to break this rule, and call a hook from inside a normal Javascript function. function catchMeIfYouCan(){ const [firstName, setFirstName] = useState('John'); }; Running this code immediately throws an error: Uncaught Error: Hooks can only be called inside the body of a function component. Now hold on a minute! If the call is just a normal function call, and a function in Javascript can't really do much in the way of analyzing the method that called it, how did React know that the method was being called from outside a React component? Magic? Not really, but since this ties into the next magicky item on our list, I'll address them both together. useState useState The hook appears to track the of the component, which was never passed to it. useState state Consider two components and , both of which have the state properties , and . Student Teacher firstName lastName age class StudentComponent extends React.Component{constructor(props){super(props);this.state = {firstName: 'Bobby',lastName: 'Tables',age: 8}}render(){return <div>{this.state.firstName} {this.state.lastName} ({this.state.age})</div>}} class TeacherComponent extends React.Component{constructor(props){super(props);this.state = {firstName: 'John',lastName: 'Keating',age: 34}}render(){return <div>{this.state.firstName} {this.state.lastName} ({this.state.age})</div>}} In the above example, in both of the components, is an instance property, and therefore can be accessed from within any of the class methods. Since it is an instance property and not a commonly shared object, the values are also encapsulated correctly and cannot access the state of and vice-versa. state TeacherComponent StudentComponent Now let’s convert these components into their function component counterparts using the hook. useState function StudentComponent(props){const [firstName, setFirstName] = useState('Bobby');const [lastName, setLastName] = useState('Tables');const [age, setAge] = useState(8); return <div>{firstName} {lastName} ({age})</div> }; function TeacherComponent(props){const [firstName, setFirstName] = useState('John');const [lastName, setLastName] = useState('Keating');const [age, setAge] = useState(34); return <div>{firstName} {lastName} ({age})</div> }; If you notice here, in both of the components, we are calling the same method, which is a method imported from the React library, and commonly shared amongst both the components. Like we discussed earlier, uses only the call order to keep track of the state values. useState useState Given this, if we render first, setting the , and state properties, and then render immediately after it, then the should be able to access the state values set by the right? StudentComponent firstName lastName age TeacherComponent TeacherComponent StudentComponent Well actually no. Even whilst using hooks, React ensures that the values are always properly encapsulated, and cannot be accessed by the wrong component. Otherwise, life wouldn't exactly be pleasant, with every component being able to access the state of all other components, would it? state But given that we are no longer using to encapsulate the state within an instance of a component, how can React perform this encapsulation for us? The answer to that is actually quite simple. But before we get to it, we need to make a slight detour into the internals of React. Or rather, the internals of React-DOM. this Better Rendering Performance with React Fiber In the initial versions of React, rendering components was something that happened as a single continuous action. In a React application, a change to the state of the application triggers a re-rendering of the entire application. However, in practice, for any non-trivial application, re-rendering the entire application results in terrible performance. In order to improve the performance, React performs quite a few optimizations. Most of these optimizations occur during the stage. Reconciliation is basically the process by which React synchronizes the Virtual DOM that it has built up by executing the methods on each component instance, with the actual DOM that is constructed on the browser. Reconciliation render Prior to React Fiber (released in v16 of React), React relied on call stack to manage rendering. Whenever a component had to be re-rendered it was added to the call stack, and once the other items on the stack finished rendering, this component would render. However, while there were items on the stack, no other work could be done. Rendering would block all other functions, and rendering could not be broken up into smaller chunks and run. In order to tackle this issue, Fiber was introduced. Fiber is basically a custom re-implementation of the call stack, which allows for things like splitting a big task into smaller work units, pausing and resuming these work units as required, as well as dropping the work units if they were no longer required. In order to achieve this, Fiber internally references each instance of a React component as a ‘fiber’ object. The fiber object is a JavaScript object that contains information about a component, its input, and its output. It is analogous to a stack frame, but it also corresponds to an instance of a component. What this effectively means is that each instance of a component is internally represented in React as a ‘fiber’, and these fibers have a lot of properties that are used by React to keep track of things like and . state props So what does all this have to do with encapsulating the in function components? state Well each component instance is a fiber, and fibers have a lot of internal React-specific properties. In both the class component and the function component, the actual instance of the component is represented as a fiber, that is not accessible outside of React internals. When we call in our class component, rather than immediately merge the partial state into the pre-existing state, Fiber will create an updateAction and enqueue that action. The queue upon which the action is enqueued is specific to each individual fiber, and most actions that are to be performed on a fiber, are enqueued rather than directly run. Enqueueing the actions allows Fiber to schedule and control the execution. this.setState The exact same thing happens when we call hook in a function component, or when we use the setter method returned by the hook. The corresponding action to be performed is enqueued on the fiber that represents this specific instance of the component. useState useState The only difference between the class component and the function component, is how the specific fiber corresponding to a particular component instance is identified. In the class component, the class component instance itself has a property which directly gives the corresponding fiber. This is set by React when the component is initially constructed. _reactInternalFiber However, in the function component, there exists no such key. So going back to our example: function StudentComponent(props){const [firstName, setFirstName] = useState('Bobby');const [lastName, setLastName] = useState('Tables');const [age, setAge] = useState(8); return <div>{firstName} {lastName} ({age})</div> }; function TeacherComponent(props){const [firstName, setFirstName] = useState('John');const [lastName, setLastName] = useState('Keating');const [age, setAge] = useState(34); return <div>{firstName} {lastName} ({age})</div> }; How does React encapsulate the data between the two component instances? By now, we know that the solution involves maintaining individual fibers for each component instance, and this fiber is set when the component is first constructed. But how does know which component is trying to access the state and return the appropriate state? useState Well, the solution that React uses to identify the calling component instances is extremely simple. In React, at any given point in time, there can be only one component that is currently rendering. React also knows exactly when a component render starts, and when it ends. So when a component instance is about to start rendering React sets a flag , pointing to that instance. When the component is done rendering, the flag is nulled. currentlyRenderingFiber Since a hook function can only be called from within a function component, and function components are basically just the render methods of the class components, it is guaranteed that whenever a hook is called, we will be in the middle of a render, and will be pointing to the fiber that is being rendered. The of the component instance is maintained as a property on this fiber, and that is what uses to get the state for the correct component instance. currentlyRenderingFiber state useState Remember, that error we saw earlier when a hook was called from a normal Javascript function? Uncaught Error: Hooks can only be called inside the body of a function component. So how did React know this was not called from within a function component? Well, React only sets the , and some other items when the component is rendered as a function component. When you call the hook from a normal javascript object, these items are not set, and that's when React throws the error. currentlyRenderingFiber dispatcher And the magic is dispelled! So that’s basically all there is to all the magic behind React Hooks. Now that we know exactly what’s going on behind the scenes, we should be able to better understand hooks, how they work, and why we need to stick to the peculiar rules for writing hooks. I’d love to hear your opinion on things I’ve written in this post. So do chime in with your comments below. Originally published at asleepysamurai.com .