Frontend·Beginner·10 questions

React State, Props & Styling

Manage component state with useState, lift state up, control forms, and apply CSS Modules.

Overview

State is data that lives inside a component and can change over time. When state changes, React re-renders the component to reflect the new data in the UI. You manage state with the useState hook: const [value, setValue] = useState(initialValue). The first element is the current state value; the second is the setter function. Call the setter with the new value (or a function that receives the old value and returns the new one). Never mutate state directly — always use the setter. State updates are asynchronous — don't rely on reading state immediately after calling the setter.

Props flow one direction: from parent to child. The parent owns data and passes it down; the child displays it. But children can communicate back up using callback props — the parent passes a function as a prop, and the child calls it when something happens (like a button click). This keeps data management in the parent and avoids complex data sharing. Props can be any JavaScript value: strings, numbers, booleans, arrays, objects, functions, or even JSX. When you omit a prop's value entirely (like <Button disabled />), it defaults to true.

CSS-in-React has several approaches. Inline styles use a JavaScript object with camelCased property names: style={{ backgroundColor: 'blue', fontSize: '16px' }}. CSS Modules (.module.css files) scope styles to the component automatically — you import styles as an object and use styles.className. CSS-in-JS libraries like styled-components let you write actual CSS syntax inside template literals attached to components. Plain global CSS files imported directly still work but class names are global. The most common choice in new projects is CSS Modules or a utility-first framework like Tailwind CSS.

Code Example

useState, State Updates, Lifting State, Callback Props, Controlled Inputs
jsx
import { useState } from 'react'

// ── Basic counter with useState ──
function Counter({ initialCount = 0 }) {
  const [count, setCount] = useState(initialCount)

  return (
    <div className="counter">
      <button onClick={() => setCount(prev => prev - 1)}>-</button>
      <span>{count}</span>
      <button onClick={() => setCount(prev => prev + 1)}>+</button>
      <button onClick={() => setCount(initialCount)}>Reset</button>
    </div>
  )
}

// ── Array state — never mutate directly ──
function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React', done: true },
    { id: 2, text: 'Build a project', done: false },
  ])
  const [input, setInput] = useState('')

  function addTodo() {
    if (!input.trim()) return
    setTodos([...todos, { id: Date.now(), text: input, done: false }])  // spread copy
    setInput('')
  }

  function toggleTodo(id) {
    setTodos(todos.map(t =>
      t.id === id ? { ...t, done: !t.done } : t  // create new object for changed item
    ))
  }

  function deleteTodo(id) {
    setTodos(todos.filter(t => t.id !== id))  // filter returns new array
  }

  return (
    <div>
      <div className="add-row">
        {/* Controlled input — value comes from state */}
        <input
          value={input}
          onChange={e => setInput(e.target.value)}
          onKeyDown={e => e.key === 'Enter' && addTodo()}
          placeholder="Add a todo..."
        />
        <button onClick={addTodo}>Add</button>
      </div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id} style={{ textDecoration: todo.done ? 'line-through' : 'none' }}>
            <input type="checkbox" checked={todo.done} onChange={() => toggleTodo(todo.id)} />
            {todo.text}
            <button onClick={() => deleteTodo(todo.id)}>✕</button>
          </li>
        ))}
      </ul>
      <p>{todos.filter(t => t.done).length} / {todos.length} completed</p>
    </div>
  )
}

// ── Lifting state up (parent owns shared state) ──
function TemperatureInput({ unit, temp, onTempChange }) {
  return (
    <div>
      <label>Temperature in {unit}:</label>
      <input
        type="number"
        value={temp}
        onChange={e => onTempChange(parseFloat(e.target.value))}
      />
    </div>
  )
}

function TemperatureConverter() {
  const [celsius, setCelsius] = useState(0)  // state lives in parent

  const fahrenheit = celsius * 9/5 + 32

  return (
    <div>
      <TemperatureInput
        unit="Celsius"
        temp={celsius}
        onTempChange={setCelsius}           // callback prop
      />
      <TemperatureInput
        unit="Fahrenheit"
        temp={fahrenheit}
        onTempChange={f => setCelsius((f - 32) * 5/9)}  // convert back
      />
    </div>
  )
}

Test Your Knowledge

Ready to test what you've learned? Take the quiz with 10 questions and see your score.