What are Props?

Props (short for "properties") are how React components receive data from their parent components. Think of props as function parameters - they allow you to pass information into a component to customize its behavior and appearance.

JavaScript Functions
JavaScript
// Function with parameters
function greetUser(name, age) {
  return `Hello ${name}, you are ${age} years old!`;
}

// Calling the function
greetUser("Alice", 25);
greetUser("Bob", 30);
React Components with Props
React JSX
// Component with props
function GreetUser({ name, age }) {
  return <p>Hello {name}, you are {age} years old!</p>;
}

// Using the component
<GreetUser name="Alice" age={25} />
<GreetUser name="Bob" age={30} />

🔑 Key Props Concepts

  • Props are read-only - Components cannot modify their props
  • Props flow downward - From parent to child components
  • Props are like function arguments - They configure how a component behaves
  • Props can be any JavaScript value - strings, numbers, objects, functions, etc.

Props in Action

Let's see how different types of data can be passed as props:

Different Types of Props
// UserCard component that accepts multiple props
function UserCard({ 
  name,           // string
  age,            // number
  isOnline,       // boolean
  avatar,         // string (URL)
  hobbies,        // array
  address,        // object
  onFollow        // function
}) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h3>{name} ({age}){isOnline && ' 🟢'}</h3>
      <p>{address.city}, {address.country}</p>
      
      <div>
        <strong>Hobbies:</strong>
        {hobbies.map(hobby => (
          <span key={hobby} className="hobby-tag">{hobby}</span>
        ))}
      </div>
      
      <button onClick={onFollow}>Follow</button>
    </div>
  );
}

// Using the component
<UserCard
  name="Sarah Johnson"
  age={28}
  isOnline={true}
  avatar="/images/sarah.jpg"
  hobbies={['Photography', 'Hiking', 'Cooking']}
  address={{ city: 'San Francisco', country: 'USA' }}
  onFollow={() => console.log('Following Sarah!')}
/>

What is State?

State is data that belongs to a component and can change over time. Unlike props (which come from outside), state is managed internally by the component. When state changes, React automatically re-renders the component.

Vanilla JavaScript
JavaScript
// Managing state manually
let count = 0;
const counterElement = document.getElementById('counter');
const buttonElement = document.getElementById('button');

function updateDisplay() {
  counterElement.textContent = `Count: ${count}`;
}

buttonElement.addEventListener('click', () => {
  count++;
  updateDisplay(); // Manual update
});

updateDisplay(); // Initial render
React with useState
React JSX
import React, { useState } from 'react';

function Counter() {
  // State is managed by React
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
  // React automatically re-renders when state changes
}

The useState Hook

The useState hook is the most common way to add state to functional components. It returns an array with two elements: the current state value and a function to update it.

useState Syntax
import React, { useState } from 'react';

function MyComponent() {
  // Syntax: const [stateVariable, setterFunction] = useState(initialValue);
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
  const [isVisible, setIsVisible] = useState(false);
  const [users, setUsers] = useState([]);
  const [user, setUser] = useState({ name: '', email: '' });

  return (
    <div>
      {/* Using state in JSX */}
      <p>Count: {count}</p>
      <p>Name: {name}</p>
      <p>Visibility: {isVisible ? 'Visible' : 'Hidden'}</p>
      
      {/* Updating state */}
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle Visibility
      </button>
    </div>
  );
}

📝 useState Rules

  • Always use the setter function - Never modify state directly
  • State updates are asynchronous - React batches updates for performance
  • State updates trigger re-renders - Component will re-render when state changes
  • Use functional updates for complex changes - Pass a function to the setter when the new state depends on the previous state

State Update Patterns

🔢 Simple Values

Numbers & Strings
const [count, setCount] = useState(0);
const [name, setName] = useState('');

// Direct updates
setCount(5);
setName('John');

// Functional updates (when depending on previous value)
setCount(prevCount => prevCount + 1);
setName(prevName => prevName.toUpperCase());

📋 Arrays

Array Updates
const [items, setItems] = useState([]);

// Add item
setItems([...items, newItem]);

// Remove item
setItems(items.filter(item => item.id !== targetId));

// Update item
setItems(items.map(item => 
  item.id === targetId 
    ? { ...item, name: 'Updated' }
    : item
));

🏷️ Objects

Object Updates
const [user, setUser] = useState({
  name: '',
  email: '',
  age: 0
});

// Update single property
setUser({ ...user, name: 'John' });

// Update multiple properties
setUser(prevUser => ({
  ...prevUser,
  name: 'John',
  age: 25
}));

🔄 Complex State

Multiple State Variables
// Separate concerns with multiple useState
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState([]);

// Or group related state
const [formState, setFormState] = useState({
  name: '',
  email: '',
  errors: {}
});

Props vs State: When to Use What?

Use Props When
  • Data comes from parent - Configuration, user info, etc.
  • Data doesn't change in this component - Display-only information
  • Multiple components need the same data - Shared state managed by parent
  • Customizing component behavior - Settings, callbacks, etc.
Props Example
// UserProfile receives data via props
function UserProfile({ user, onEdit }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>{user.email}</p>
      <button onClick={onEdit}>Edit</button>
    </div>
  );
}
Use State When
  • Data changes over time - User input, API responses
  • Component owns the data - Internal component logic
  • Triggering re-renders - UI updates based on user interaction
  • Temporary data - Form inputs, modal visibility
State Example
// EditForm manages its own state
function EditForm({ user, onSave }) {
  const [name, setName] = useState(user.name);
  const [email, setEmail] = useState(user.email);
  
  return (
    <form>
      <input 
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <input 
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <button onClick={() => onSave({name, email})}>
        Save
      </button>
    </form>
  );
}

Data Flow in Action

Let's build a practical example that demonstrates how props and state work together in a real application:

Complete Example: Todo App
import React, { useState } from 'react';

// Child component - receives props, no state needed
function TodoItem({ todo, onToggle, onDelete }) {
  return (
    <div className={`todo-item ${todo.completed ? 'completed' : ''}`}>
      <span onClick={() => onToggle(todo.id)}>
        {todo.text}
      </span>
      <button onClick={() => onDelete(todo.id)}>
        Delete
      </button>
    </div>
  );
}

// Child component - manages its own input state
function AddTodo({ onAdd }) {
  const [text, setText] = useState('');
  
  const handleSubmit = (e) => {
    e.preventDefault();
    if (text.trim()) {
      onAdd(text);
      setText(''); // Reset form
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Add a todo..."
      />
      <button type="submit">Add</button>
    </form>
  );
}

// Parent component - manages shared state
function TodoApp() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React', completed: false },
    { id: 2, text: 'Build a project', completed: false }
  ]);
  
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(),
      text,
      completed: false
    };
    setTodos([...todos, newTodo]);
  };
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id 
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  };
  
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  return (
    <div className="todo-app">
      <h1>My Todos</h1>
      <AddTodo onAdd={addTodo} />
      <div>
        {todos.map(todo => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onToggle={toggleTodo}
            onDelete={deleteTodo}
          />
        ))}
      </div>
    </div>
  );
}

🔄 Data Flow Breakdown

  1. TodoApp manages the main todos state (array of todo objects)
  2. TodoApp passes individual todos and callback functions as props to TodoItem
  3. TodoItem receives props and calls callbacks when user interacts
  4. AddTodo manages its own input state and calls the onAdd callback
  5. State changes in TodoApp trigger re-renders of all child components

Best Practices

✅ State Management

  • Keep state as simple as possible
  • Use multiple useState for unrelated data
  • Don't duplicate props in state
  • Lift state up when multiple components need it

✅ Props Handling

  • Use destructuring for cleaner code
  • Provide default values for optional props
  • Use meaningful prop names
  • Consider PropTypes for validation

✅ Performance

  • Don't create objects/functions in render
  • Use functional updates for performance
  • Consider React.memo for expensive components
  • Keep components small and focused

❌ Common Mistakes

  • Never modify state directly
  • Don't use array indices as keys
  • Avoid too much state in one component
  • Don't forget dependencies in useEffect

What's Next?

You now understand how data flows through React applications using props and state. Next, you'll learn how to make your components respond to user interactions.

🚀 Continue Learning

  1. Event Handling - Make your components interactive
  2. Conditional Rendering - Show different content based on state
  3. React Hooks - Master useEffect and other hooks
  4. Advanced State Management - Context API and beyond