🧠 Debugging React Internals: Why I Started Cloning Functions and Objects (Seriously)

“To understand the code, you must live in the code.”
— A React explorer withconsole.log
as his compass
Where it all began... 🌱
When I decided to truly understand how React works under the hood — not just use it, but really get it — I didn’t know where to begin. There was no roadmap. No “Start Here” sign.
So I did what any curious developer would do.
I opened the React source and started randomly sprinkling console.log()
like confetti 🎉
And then... I discovered something curious.
If you’ve ever used React, you know we usually begin with:
ReactDOM.render(<App />, document.getElementById('root'));
So I placed a console.log()
right before and after ReactDOM.render()
. And what I saw in the Chrome DevTools console blew my mind 🤯
It looked like the Fiber tree had already been constructed immediately after calling ReactDOM.render()
.
But wait... that’s impossible.
I added console.log()
inside the createFiber()
function later — and that was called only afterwards.
Something didn’t add up.
🪞 The Console Illusion
Here’s the trap I fell into — and maybe you have too:
const obj = {};
console.log(obj);
obj.prop = 123;
Now, what do you think Chrome’s console shows?
Just {}
?
Nope. You get something like {prop: 123}
with a little blue “i” icon next to it. Why?
Because console.log()
doesn’t snapshot the object at the time of logging. It logs a reference. The console lazily evaluates and renders it later — after it’s mutated.
So all my logs during React Fiber construction?
They showed the state of the object after mutation, not when the log happened. It was a time-traveling illusion ⏳✨
💡 MDN Knows This Too
MDN even talks about this in their docs:
🔗 Outputting text to the console - MDN
And their suggested workaround?
console.log(JSON.parse(JSON.stringify(obj)));
Sure, this works for plain objects. But when you're navigating the jungles of React internals — you're not just dealing with objects.
You're dealing with HTMLElements, linked lists, Fibers, Functions, and a whole zoo of entities.
🧬 When cloning gets serious...
Let’s take HTML elements. JSON.stringify()
? Doesn’t work.
Instead, you might use:
console.log(node.cloneNode(true));
Works better. But what about Fiber nodes? They’re not regular objects. They contain functions, cyclic references, and complex structures.
That’s where my favorite weapon comes in:
lodash.cloneDeep()
🔥
import cloneDeep from 'lodash/cloneDeep';
console.log(cloneDeep(fiberNode));
It just works. Deeply. Recursively. Reliably.
It can clone:
✅ HTMLElements
✅ Functions (to an extent)
✅ Cyclic structures
✅ React's Fiber nodes
It helped me capture the actual shape of Fiber at the exact moment it was being created — not the shape it had later.
And when you're debugging the internals of React, that's everything.
🛠️ So, when should you clone a function or an object?
Here's the takeaway:
If you're debugging or logging, and you care about the exact state at the time of execution, clone it.
If you’re dealing with complex data (DOM nodes, Fibers, functions),
JSON.stringify()
will fail silently or just skip important parts.For React internals and anything non-trivial:
cloneDeep()
is your best friend.
✨ Final Thoughts
Sometimes the tools we use to understand the system can deceive us.
React is magical — but to truly learn its magic, you need to look past the illusions.
“We don’t get to the truth by looking at the output. We get there by understanding the process.”
— Me, probably after 10 cups of coffee ☕️
Whether you’re hacking React internals, reverse-engineering Fiber, or just debugging a mysterious bug — clone consciously.
🧪 Inspect deeply.
🧠 Think like the framework.
🚀 Learn beyond the docs.
Subscribe to my newsletter
Read articles from Thamizh Arasan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
