From my experience as an instructor state and props can really throw React beginners for a loop. The good news is that loop doesn't need to be endless (😅).
State & Props: What Are They?
props
(short for “properties”) andstate
are both plain JavaScript objects. While both hold information that influences the output of render, they are different in one important way:props
get passed to the component (similar to function parameters) whereasstate
is managed within the component (similar to variables declared within a function).
Uh, yeah, okay... but what does that mean? Well, let's take a look at a real world example. Think way back to your grade school days. Remember field trips? And permission slips?
Your mom, dad or parental guardian had to sign a permission slip in order for you to go on a field trip. You brought that permission slip to your teacher and showed it to them to prove that you were allowed to go. This is a great way to think about state and props.
I'm going to use hooks and functional components in these examples, but class components will work, too.
Starting out, the browser looks like this:
The Parent
component is rendered in App.js
. Here is the starting code for both Parent
and Child
:
import Child from './Child';
const Parent = () => {
return (
<div className="container">
<div className="parent">
<h2>I am the parent.</h2>
</div>
<div className="child">
<Child />
</div>
</div>
);
};
export default Parent;
and here is the starting code for Child
:
const Child = () => {
return (
<div className="child-component">
<h3>I am the child.</h3>
</div>
);
};
export default Child;
The first thing we're going to do is set up state
in our Parent
component.
const [isSigned, setIsSigned] = useState(false);
const location = "the science museum";
The permission slip for the science museum begins in an unsigned state. Now we need to set up some way for our Parent
to sign the permission slip for their child. We'll stick to a simple click event on a button. The button will also render conditionally, based on the value of our isSigned
state.
const handleClick = () => {
isSigned ? setIsSigned(false) : setIsSigned(true);
};
const renderButton = () => {
return !isSigned ? <button onClick={handleClick}>Sign Permission Slip</button> : <button onClick={handleClick}>You're Grounded!</button>
};
We now want to invoke renderButton
right under our h2
tag in the Parent
component. This is what we see in the browser now:
In order to make sure our button actually works, we're going to add {console.log(isSigned)}
inside of our Parent
component. Our code should look something like this:
const Parent = () => {
const [isSigned, setIsSigned] = useState(false);
const location = "the science museum";
const handleClick = () => {
isSigned ? setIsSigned(false) : setIsSigned(true);
};
const renderButton = () => {
return !isSigned ? <button onClick={handleClick}>Sign Permission Slip</button> : <button onClick={handleClick}>You're Grounded!</button>
};
return (
<div className="container">
{console.log(isSigned)}
<div className="parent">
<h2>I am the parent.</h2>
{renderButton()}
</div>
<div className="child">
<Child />
</div>
</div>
);
};
This is what we should see after our first button click:
and if we click one more time:
Now that we know everything is working correctly in our Parent
component, we can start thinking about props
! Our Child
needs some way to tell their teacher whether they can or cannot go on the field trip. We need to pass this information down to our Child
.
<div className="child">
<Child location={location} isSigned={isSigned} />
</div>
This is how we pass information from parent to child. In our Child
component, we pass the props
in as an argument.
const Child = (props) => {
console.log(props)
return (
<div className="child-component">
<h3>I am the child.</h3>
</div>
);
};
With that console.log
, we'll see this in browser console:
We can make things a little cleaner here by using destructuring!
const Child = ({ location, isSigned }) => {
console.log(location)
console.log(isSigned)
return (
<div className="child-component">
<h3>I am the child.</h3>
</div>
);
};
export default Child;
Now that we have access to that data in our Child
component, we can display that data!
In the Child
component, we now have a function called renderPermission
, which renders text conditionally based on the value of isSigned
.
const Child = ({ location, isSigned }) => {
const renderPermission = () => {
if (isSigned) {
return `I can go on the field trip to the ${location}!`
} else {
return `I'm not allowed to go on the field trip to the ${location}.`
};
};
return (
<div className="child-component">
<h3>I am the child.</h3>
{renderPermission()}
</div>
);
};
export default Child;
Remember that we can't change props! A kid can't forge their parent/guardians signature! Let's try it out.
const forgePermission = () => {
console.log('Clicked')
isSigned = true;
};
return (
<div className="child-component">
<h3>I am the child.</h3>
<button onClick={forgePermission}>Forge Signature</button> <br />
{renderPermission()}
</div>
);
We're including a console.log
so that we can be sure that our event listener is working.
We can't do it! They're not changing! Our Child
component is not re-rendering. Our parent component is in charge of the data and changing it (with state!) and our child component only has the ability to display that data (they're props!).
Here is a look at our finished code:
import { useState } from 'react';
import Child from './Child';
const Parent = () => {
const [isSigned, setIsSigned] = useState(false);
const location = "the science museum";
const handleClick = () => {
isSigned ? setIsSigned(false) : setIsSigned(true);
};
const renderButton = () => {
return !isSigned ? <button onClick={handleClick}>Sign Permission Slip</button> : <button onClick={handleClick}>You're Grounded!</button>
};
return (
<div className="container">
<div className="parent">
<h2>I am the parent.</h2>
{renderButton()}
</div>
<div className="child">
<Child location={location} isSigned={isSigned} />
</div>
</div>
);
};
export default Parent;
const Child = ({ location, isSigned }) => {
const renderPermission = () => {
if (isSigned) {
return `I can go on the field trip to the ${location}!`
} else {
return `I'm not allowed to go on the field trip to the ${location}.`
};
};
const forgePermission = () => {
console.log('Clicked')
isSigned = true;
};
return (
<div className="child-component">
<h3>I am the child.</h3>
<button onClick={forgePermission}>Forge Signature</button> <br />
{renderPermission()}
</div>
);
};
export default Child;
That's it! That's state
and props
in React. It's as simple as that.
Cover image from Austin Pacheco on Unsplash
Top comments (0)