What Is the Difference Between State and Props in React JS?
Props are immutable inputs passed from parent components, whereas state is mutable data managed internally within a component that triggers re-renders when updated.
Understanding the difference between state and props in React JS is essential for mastering component architecture in the facebook/react repository. While both determine what appears on the screen, props represent external configuration flowing downward, and state represents internal memory that evolves over time based on user interactions or side effects. React enforces strict separation between these concepts through specific implementation details in the reconciler and component base classes.
Core Definitions
Props: The Immutable Public API
Props (short for "properties") are JavaScript objects containing attributes passed from a parent component to its children. According to packages/react/src/jsx/ReactJSXElement.js, React explicitly freezes the props object using Object.freeze(element.props) immediately after element creation. This architectural choice enforces immutability, ensuring that receiving components cannot modify their own props and must treat them as read-only configuration.
State: The Mutable Internal Store
State represents data owned and managed by the component itself. In packages/react/src/ReactBaseClasses.js, the Component base class initializes this.state and provides the setState method, which merges partial state updates and schedules re-renders. Unlike props, state is writable and persists across renders on the component's associated fiber node in React's internal tree.
Architectural Implementation
The React source code hardcodes distinct lifecycles and update mechanisms for these data types.
Props Enforcement via Object Freezing
When JSX transforms into ReactElement objects, the constructor captures the props object and freezes it to prevent accidental mutations. This immutability allows React to perform efficient reference comparisons during reconciliation, knowing that props changes can only originate from parent updates, not internal modifications.
State Updates Through the Fiber Architecture
State lives on the fiber node associated with each component instance. When setState is called in class components—or when the updater function from useState is invoked in function components—React marks the fiber as needing an update. The reconciler then processes this state change, computes the new component output, and commits the updates to the DOM.
Practical Code Examples
Props: Parent-to-Child Data Flow
Props configure components from the outside and remain read-only within the child.
// Parent.jsx
function Parent() {
const handleClick = () => alert('Clicked!');
return <Child label="Press me" onClick={handleClick} />;
}
// Child.jsx
function Child({ label, onClick }) {
// Props are destructured but cannot be modified here
return <button onClick={onClick}>{label}</button>;
}
The label and onClick values originate in Parent and pass to Child as props. The Child component cannot modify these values; it consumes them purely for rendering and event delegation.
State: Internal Component Memory
State handles data that changes due to internal logic or user interaction.
// Counter.jsx
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const increment = () => setCount(c => c + 1);
return (
<div>
<p>Current count: {count}</p>
<button onClick={increment}>Add</button>
</div>
);
}
Here, count lives in state, initialized via the useState hook. Each invocation of setCount triggers React's update cycle, scheduling a re-render that displays the incremented value.
Class Component: Explicit Separation
In class components, the distinction appears clearly in the component structure.
// Greeting.jsx
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = { visible: true };
}
toggle = () => this.setState(s => ({ visible: !s.visible }));
render() {
const { name } = this.props; // Immutable external input
const { visible } = this.state; // Mutable internal data
return (
<div>
{visible && <h1>Hello, {name}!</h1>}
<button onClick={this.toggle}>Toggle</button>
</div>
);
}
}
The name prop arrives from the parent and never changes within Greeting, while visible state toggles locally via this.setState.
When to Use Props vs State
Selecting the correct data storage method depends on ownership and mutability requirements.
-
Use Props for data originating outside the component, including configuration values, content strings, or callback functions. Props establish the component's public contract and enable composition patterns.
-
Use State for data that changes over time due to user actions, asynchronous responses, or timer events. State should remain private to the component and not be directly manipulated by parent or sibling components.
Summary
- Props are immutable objects passed from parent to child, frozen by
Object.freeze()inReactJSXElement.jsto prevent mutations, and serve as the component's public API. - State is mutable data stored on component fibers, managed via
setStateoruseState, and triggers re-renders when updated through React's scheduler. - Prop changes always flow from parent updates, while state changes are controlled internally by the component itself via specific update methods.
- React implements these patterns in
packages/react/src/ReactBaseClasses.jsfor state management andpackages/react/src/jsx/ReactJSXElement.jsfor props handling.
Frequently Asked Questions
Can a React component modify its own props?
No. React explicitly freezes props objects to enforce immutability. Attempting to modify this.props or argument props directly will not update the UI and may introduce subtle bugs. Components should treat props as read-only and communicate changes back to parents via callback functions passed through props.
What triggers a re-render when using state?
When you call setState in class components or the setter function from useState in function components, React schedules an update for that component's fiber node. During the reconciliation phase, React compares the new state against the previous state, re-renders the component with the updated values, and commits the resulting changes to the DOM.
Should fetched API data live in props or state?
If the component fetches the data itself, store it in state to track loading states and responses. If a parent component fetches the data and passes it down, it arrives via props. For data needed by multiple sibling components, lift the state to their common ancestor or use a dedicated state management solution.
Why does React enforce immutability for props but not state?
Immutability for props ensures predictable top-down data flow, making applications easier to reason about and optimize. Since props originate from parents, allowing child components to modify them would create side effects and complicate debugging. State, however, belongs to the component itself, so React provides specific mechanisms (setState, useState updaters) to mutate it safely while still triggering efficient re-renders.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s https://instagit.com/install.md