When I first read the official documentation as a preliminary to studying React in earnest, I quickly became dismayed. HTML-looking elements commingling amidst JS? This ubiquitous props object, which comes from where? The mysteriously recorded information kept in state, necessary for dynamic behavior—but how? And on top of all of this, the new overarching concept of information flow across a component hierarchy! I closed the React docs without making it to the end.
However, after my head stopped spinning, I found at least one thing had made sense: components—or rather, the idea of modularity. As I started actually studying each piece of React and learning its declarative syntax, I began to form an analogy for how it all works, which ended up helping me immensely to wrap my mind around the basic concepts and how to use them to build things. If you're also having a hard time with React, maybe my analogy will help you too.
It has to do with what it was like to set up old computer hardware like this:
My first computer, long ago, was a Mac Plus like that one (though I don't think I ever had one of those additional external floppy drives). The other hand-me-down computers I used afterward were similar desktop setups: a tower, a monitor, a keyboard, a mouse, maybe eventually a Wacom tablet or a Zip disc drive. Unlike the unitary laptop I use now, back then my computer as a whole system was cobbled together from physically separate pieces of hardware that had to be correctly connected to each other in order to function. Of course, before the advent of USB, that meant several different kinds of cables had to be matched with the right kinds of ports in the right places to successfully make those connections.
Perhaps you already see where this is going: my old computer was a collection of components, linked together by specific kinds of connections. When I starting thinking of React apps in the same way, visualizing React components as if they were physical pieces I had to plug into each other with the right figurative cables, it felt like the whole framework suddenly opened up to me.
A Series of Tubes
Take props, for starters. At first, I struggled to understand how props were defined for any given component, and also how specific values were passed as props to a particular component instance. But it all became clear once I saw examples of destructured props. Say we have some child component like this in App.js:
<ChildComponent firstProp={someValue} secondProp={anotherValue} />
I understood that we were passing someValue
and anotherValue
as props to our child component from its parent component, but I didn't understand how the connection worked until I saw the incoming props destructured in the child component like this:
const ChildComponent = ({firstProp, secondProp}) => {
//...
}
Now it was as if I could see both ends of the cable: each prop is like a wire I had to plug into the right 'port' of the component tag in the parent component, and into the corresponding 'port' in the component declaration down the hierarchy in ChildComponent.js.
It didn't matter that I didn't entirely understand how object destructuring works, or even that this was an example of that. I just accepted this was the applicable syntax, and understood that only with those figurative cables properly connected can the child component get the specified values of those props from its parent, and access those values with the specified variable references passed down to it.
Once that much clicked, I retroactively understood what it meant if things were instead written like this:
const ChildComponent = (props) => {
const firstProp = props.firstProp
const secondProp = props.secondProp
//...
}
Without destructuring, it's as if the individual prop 'wires' are bundled together, enclosed inside a tube. We have to pull them out and connect each one individually to use them within the child component.
One Thing Leads to Another
Having embraced this notion of plugging components into each other, the basic idea of state became much more easily comprehensible in turn. Say I have some state like this in a component:
const [isToggled, setIsToggled] = useState(false)
Again, accepting this is just the syntax for setting things up (rather than worrying about how this clever array destructuring actually works when we initialize state like this), I thought about what's going on here as physically as possible: isToggled
, the state variable, is just a little container that holds some value I want to store (in this example, just a true/false Boolean). The setter function setIsToggled
is a convenient tool React provides for the sole purpose of changing the contents of that state variable container to whatever value we pass it.
Within the component where this state is kept, I can connect whatever pieces I need to the state by having them use the setter function to change what's in the state variable, or by using a reference to the state variable to look at its current value. It's almost like another pair of little wires I can link up inside the component itself to create functionality—in this case probably a button that uses the setter function to toggle the value of the state variable between true and false, like this:
<button onClick={setIsToggled(!isToggled)}>ON/OFF</button>
In this example, it's as if I'm attaching a wire to my button to connect it to the state setter function, building a circuit that'll be completed when the onClick
event listener fires.
And Another Thing
With the essentials of state in hand, I could combine it with my understanding of props as cables running between components to get a grip on the bizarre concept of inverse data flow. To expand upon our toggle example, what if the toggle button turns dark mode styling on and off for the whole page? In that case, the button itself might exist in a child component (like a navbar or a header), but we need to keep the isToggled
state higher up, in App.js, so we can use it to control which CSS is applied to everything in the App.
With my understanding of props as cables to connect components together, I was finally able to understand how to pass down a handler function as a prop from the parent component App.js to the child component. That function would look like this:
const handleToggle = () => {
setIsToggled(!isToggled)
}
We'd pass it like any other prop, connecting both ends of the prop 'cable' in the parent component and the child component:
// Somewhere in the return of App.js:
<ChildComponent handleToggle={handleToggle} />
//Meanwhile, in ChildComponent.js:
const ChildComponent = ({handleToggle}) => {
//...
}
And finally, our revised button would be able to call the handler function in its event listener, like this:
<button onClick={handleToggle}>ON/OFF</button>
Since we've passed handleToggle
as a prop, we can call it in the child component, but when we do, it runs at the level of the parent component where it's actually declared—and where it has access to the state setter function setIsToggled
to change the value we're storing in the state variable isToggled
. Passing a handler function as a prop in this way is like plugging in a special type of cable that allows us to send information back up the component hierarchy, unlike regular prop cables, which only carry information between components going down the hierarchy.
All this is really basic stuff, but when I first started with React, the paradigm underlying the library and its whole approach to building a frontend was overwhelming at first—to say nothing of the unfamiliar syntax of JSX, functional components, props, and state. Finding a way to visualize React was the breakthrough that enabled me to understand these fundamentals. Once I found and plugged in that first figurative cable, the rest started coming much more easily. Now I feel I already have a decent grasp on how to build things with React, and I'm excited to see what I can do with it.
Top comments (0)