Blogs / react-props-state



React Props vs State

#react, #props, #state, #immutability

Dec 15, 20249 min read



Props vs State

So you landed a React interview —lucky 🥲— and right of the bat they hit you with : “Explain the difference between state and props in React. 🙂”

You panicking mid interview

You froze, panicked, and did the only thing that made sense—you switched your laptop to airplane mode and pretended to get disconnected. I don’t blame you, we've all been there. Nerves, right?

But hey, let’s make sure you ace it next time. Let's break down state vs. props in React.

If you’re new to React, it’s quite easy to confuse props and state because, at first glance, they may seem like the same thing—kinda. However, they’re like different sides of the same coin. Props and state are related but serve different purposes.

Props and state are both plain JavaScript objects. While both hold information that influences the output of a render, they have different roles. Props are passed to the component, much like function parameters, while state is managed within the component itself, similar to variables declared within a function.

Components are functions?

To understand these concepts, let's draw similarities to a plain JavaScript function. React components resemble JavaScript functions, because they are functions. A component is just a function that returns some JSX - HTML, JavaScript, and sometimes CSS.

Like the documentation says. ‘A React component is a JavaScript function that you can sprinkle with markup.’

Before we dive into the react side of things lets mirror how React state would work in a plain JavaScript function by looking at an an example of, you guessed it, the one and only, a counter. 🥳

Us Celebrating for Counter Example

In plain JavaScript, your approach might look like this:

function counter() {
  let count = 0; // Initial state
  
  function increment() {
    count++; // Update state
    console.log("Count:", count);
  }
  
  return increment; // Return function to access state
}

const incrementCounter = counter();
incrementCounter(); // Count: 1
incrementCounter(); // Count: 2

Here, count acts as the local state variable within the counter function, tracking the number of increments. Initially set to 0, each call to increment updates the count.

const instanceOne = counter();
instanceOne(); // Count: 1
instanceOne(); // Count: 2

const instanceTwo = counter();
instanceTwo(); // Count: 1
instanceTwo(); // Count: 2
instanceTwo(); // Count: 3
instanceTwo(); // Count: 4

instanceOne(); // Count: 3

Despite multiple invocations, each instance maintains its own isolated state. When instanceTwo is called, the count starts anew at 1. Upon subsequent calls to instanceOne, the count resumes at 3.

This is because state is private. Neither instances have access to the other’s local state.

Creating a new instance of counter creates a new scope where count is encapsulated and private. This means that the count variable within one instance of counter is independent of the count variable within another instance, and changes to one do not affect the other.

Components Remember Information Through State

State in a component is like the component’s local variables. When a component needs to keep track of information between renderings (fancy word for function calls) the component itself can create, update, and use state.

A component manages its own state internally, ensuring it's private and not accessible or modifiable by other components. The state starts with a default value when a component mounts and then is eventually mutated ****(usually from user events).

In our plain JavaScript example we used regular variables but that won’t work inside a React component.

import React, { useState } from 'react';

function Counter() {
  let count = 0;

  const increment = () => {
    count = count + 1
  };

  console.log(count)

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </>
  );
}

export default Counter;

You can test out this snippet in the sandbox here.

As the React documentation explains, using and updating a local variable like count in this manner won't trigger re-renders of the component. React doesn't detect changes to local variables, and the component won't reflect the updated state.

To solve this issue we typically use the useState hook. This hook provides a state variable and a setter function, ensuring that changes to state trigger re-renders.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </>
  );
}

export default Counter;

In this refactored version, the useState hook initializes the count state to 0 and provides a function setCount to update the state. This allows our Counter component to keep track of the count locally and re-render (call the function again) whenever the count changes.

Also much like our JavaScript example, we can have multiple instances of the same component.

import React from "react";
import Counter from './Counter'

export default function App() {
  return (
    <>
     <Counter/>
     <Counter/>
    </>
  );
}

Both counters can be incremented independently without interfering with each other. The state remains private to each component, just like local variables in a function's scope.

Props … Properties ?

What if, in our plain JavaScript counter example, we wanted another function to handle logging the count details?

Like this.

function logCount(value) {
  console.log("Count:", value);
}

function counter() {
  let count = 0; // Initial state

  function increment() {
    count++; // Update state
    logCount(count);
  }

  return increment; // Return function to access state
}

We get the same result as before when calling counter, but we've offloaded the responsibility of displaying the count details to the logCount function.

const instanceOne = counter();
instanceOne(); // Count: 1
instanceOne(); // Count: 2

By passing the counter's local state variable as an argument to the logCount function, we've made logCount reusable, allowing it to accept inputs and handle the display of count details.

In React, a similar behaviour is achieved through props.

Props are Inputs?

Props—short for properties—are a component's configuration. They are the information that you pass to a HTML element or JSX tag. For example, className, src, alt, width, and height are some of the props you can pass to an <img> .

Props aren’t just reserved for HTML elements though. Your custom components can have props as well. Props allow components to communicate and share data with each other. They make components reusable by giving components the ability to receive data from their parent. They are equivalent to function parameters.

For instance, consider a parent component, ParentComponent, passing data to a child component, ChildComponent, using props:

// Parent component passing data to child component using props
function ParentComponent() {
  return (
    <>
      <ChildComponent message="Hello from ParentComponent!" />
    </>
  );
}

function ChildComponent(props) {
  return <p>{props.message}</p>;
}

By adding a message prop, the ParentComponent sends data to the ChildComponent, which then renders the message accordingly.

Similar to our plain JavaScript example, we can create a new component to offload displaying the count.

import React from 'react';

export default function Display(props) {
  return <p>Count: {props.count}</p>;
}

Here, the Display component accepts a count prop. Since props is an object we can access it with props.count.

And let's consider our Counter component, which manages the count state:

import React, { useState } from 'react';
import Display from './Display';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => setCount(count + 1);
  
  return (
    <>
      <Display count={count} />
      <button onClick={increment}>Increment</button>
    </>
  );
}

export default Counter;

The count state is passed to the Display component as a prop, allowing it to access and render the current count value.

This setup demonstrates how state from one component (Counter) can be passed to another component (Display), much like how a function could call another function and give it arguments.

Props are Immutable

Props are external data passed into components and are immutable. They are read-only in the receiving component. This ensures a one-way flow of data, with information flowing from parent to child components. As a result, the receiving component cannot modify its props.

In our Display component, attempting to directly update props.count will result in an error. React enforces immutability to maintain the integrity of the data flow and prevent unexpected side effects.

import React from 'react';

function Display(props) {
	// not allowed
  props.count = 2;
  
  return <p>Count: {props.count}</p>;
}

export default Display;

React needs to know when the state changes so it can re-render our component and so if we need to update count we use the state setter function that useState provides.

Conclusion

So while props and state are similar, they play different roles. Props are passed to the component, much like function parameters, while state is managed within the component itself, similar to variables declared within a function.

Let’s revisit their main differences.

State

  1. States are mutable; they can be modified using the state setter function returned by useState.
  2. Each component has its own state, which is specific to that component and cannot be accessed or modified by other components.
  3. States are initialized when a component mounts, providing an initial value that can be updated throughout the component's lifecycle.

Props

  1. Props are immutable; once set, they cannot be modified directly.
  2. Props allow components to communicate and share data with each other. They allow data to be passed from parent components to child components.

Understanding props and state is like having the keys to the React kingdom! They're the building blocks for how components handle and share data. I hope this article has shed some light on the difference between props and state in React for you. Happy coding!


Did you find this helpful?

Check out my other articles on JavaScript and React for more helpful insights and practical tips. You can read them on my Website, Medium, or Hashnode.

Here are some of my recent articles.

  1. How to Implement Infinite Scroll with React Query & Supabase Pagination in Next.js
  2. Creating Immutable Objects in JavaScript
  3. How to Use React Hook Form + Zod with Next.js Server Actions

Thanks for reading!


Back to Blogs