[{"data":1,"prerenderedAt":116},["ShallowReactive",2],{"qa-\u002Freact\u002Fstate-and-data-flow\u002Flifting-state":3},{"page":4,"siblings":100,"blog":113},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":20,"order":21,"path":22,"questions":23,"questionsCount":90,"related":91,"seo":92,"seoDescription":93,"stem":94,"subtopic":95,"topic":96,"topicSlug":97,"updated":98,"__hash__":99},"qa\u002Freact\u002Fstate-and-data-flow\u002Flifting-state.md","Lifting State",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"easy","md","React","react",{"subtopicSlug":19},"lifting-state",true,1,"\u002Freact\u002Fstate-and-data-flow\u002Flifting-state",[24,28,32,36,40,45,49,53,57,61,65,69,73,77,81,85],{"id":25,"difficulty":14,"q":26,"a":27},"lifting-state-what","What does \"lifting state up\" mean in React?","**Lifting state up** means moving shared state to the **lowest common\nancestor** of the components that need it. The ancestor owns the state\nand passes both the value and a setter callback down as props, so siblings\ncan read the same data and trigger updates through a common parent.\n\n```jsx\nfunction Parent() {\n  const [value, setValue] = useState('')\n\n  return (\n    \u003C>\n      \u003CInput value={value} onChange={setValue} \u002F>\n      \u003CPreview value={value} \u002F>\n    \u003C\u002F>\n  )\n}\n\n\u002F\u002F Input writes, Preview reads — state lives in Parent\n```\n\n**Rule of thumb:** If two components need to stay in sync, lift their\nshared state to the closest ancestor that contains both of them.\n",{"id":29,"difficulty":14,"q":30,"a":31},"lifting-state-why","Why is lifting state up necessary instead of each component keeping its own copy?","React enforces **one-way data flow** — a child cannot directly read or\nwrite a sibling's state. If two components each hold their own copy of\nthe same value, those copies can diverge and the UI becomes inconsistent.\n\n```jsx\n\u002F\u002F ❌ Two independent copies — can get out of sync\nfunction A() { const [x, setX] = useState(0); ... }\nfunction B() { const [x, setX] = useState(0); ... }\n\n\u002F\u002F ✅ One source of truth in the common parent\nfunction Parent() {\n  const [x, setX] = useState(0)\n  return \u003C>\u003CA x={x} onChange={setX} \u002F>\u003CB x={x} \u002F>\u003C\u002F>\n}\n```\n\nA single source of truth makes behaviour predictable: any change flows\ndown from the owner and every consumer automatically re-renders with\nthe latest value.\n\n**Rule of thumb:** If you ever copy state from props into another\n`useState`, you almost certainly have a sync problem waiting to happen.\n",{"id":33,"difficulty":14,"q":34,"a":35},"lifting-state-callback","How does a child component communicate a change back to its parent?","The parent passes a **callback prop** (e.g. `onChange`, `onSubmit`) to\nthe child. When the user interacts, the child calls that callback with\nthe new value. The parent's setter updates state, which flows back down.\n\n```jsx\nfunction Parent() {\n  const [qty, setQty] = useState(1)\n  return \u003CQuantityInput value={qty} onChange={setQty} \u002F>\n}\n\nfunction QuantityInput({ value, onChange }) {\n  return (\n    \u003Cinput\n      type=\"number\"\n      value={value}\n      onChange={e => onChange(Number(e.target.value))}\n    \u002F>\n  )\n}\n```\n\nThe child owns zero state here — it is a **controlled** component driven\nentirely by props.\n\n**Rule of thumb:** Name callbacks `onX` in the parent and `handleX`\ninside the child to keep the intent clear.\n",{"id":37,"difficulty":14,"q":38,"a":39},"lifting-state-siblings","Two sibling components need to share data. Where should the state live?","In their **closest common ancestor**. That ancestor becomes the single\nsource of truth and distributes both the value and update callbacks.\n\n```jsx\n\u002F\u002F Siblings: SearchBar and ResultsList need the same query string\nfunction SearchPage() {\n  const [query, setQuery] = useState('')\n\n  return (\n    \u003Cdiv>\n      \u003CSearchBar query={query} onSearch={setQuery} \u002F>\n      \u003CResultsList query={query} \u002F>\n    \u003C\u002Fdiv>\n  )\n}\n```\n\nAvoid the temptation to let one sibling own the state and pass it\nsideways — React has no sibling-to-sibling channel.\n\n**Rule of thumb:** Siblings communicate via a shared parent, never\ndirectly.\n",{"id":41,"difficulty":42,"q":43,"a":44},"lifting-state-too-high","medium","What goes wrong if you lift state higher than necessary?","Every component that receives the state as a prop will re-render when\nthat state changes — including ones that don't use it but happen to\nsit between the owner and the consumer. This causes **unnecessary\nre-renders** and can hurt performance in large trees.\n\n```jsx\n\u002F\u002F If `query` lives in App, every child of App re-renders on each\n\u002F\u002F keystroke — even Header, Sidebar, Footer that don't care about query\nfunction App() {\n  const [query, setQuery] = useState('')\n  return (\n    \u003C>\n      \u003CHeader \u002F>       {\u002F* re-renders needlessly *\u002F}\n      \u003CSidebar \u002F>      {\u002F* re-renders needlessly *\u002F}\n      \u003CSearchPage query={query} onSearch={setQuery} \u002F>\n    \u003C\u002F>\n  )\n}\n```\n\nKeep state as **low** in the tree as possible while still being shared\nwhere needed.\n\n**Rule of thumb:** Lift only as high as required; don't promote state\npre-emptively \"in case\" something else needs it.\n",{"id":46,"difficulty":42,"q":47,"a":48},"lifting-state-vs-context","When should you lift state vs. reach for Context or a global store?","| Scenario | Approach |\n|---|---|\n| 1–2 levels of passing, few consumers | Lift state (props) |\n| Deeply nested tree, many consumers, changes infrequently | Context |\n| Frequently updated, many subscribers, complex logic | External store (Redux, Zustand) |\n\nLifted state is the **simplest** solution and should be the default.\nContext adds an implicit dependency and can cause broad re-renders.\nA global store adds a dependency and operational complexity.\n\n```jsx\n\u002F\u002F Fine with lifting (shallow):\n\u003CPage>\u003CFilterBar \u002F>\u003CTable \u002F>\u003C\u002FPage>\n\n\u002F\u002F Consider Context (deeply nested):\n\u003CApp>\u003CLayout>\u003CSidebar>\u003CDeepWidget \u002F>\u003C\u002FSidebar>\u003C\u002FLayout>\u003C\u002FApp>\n```\n\n**Rule of thumb:** Start with lifting. Reach for Context only when prop\ndrilling becomes painful; reach for a store only when Context causes\nperformance problems.\n",{"id":50,"difficulty":14,"q":51,"a":52},"lifting-state-controlled","What is the relationship between lifting state and controlled components?","When you lift state into a parent and pass `value` + `onChange` to an\ninput, that input becomes a **controlled component** — the parent's\nstate is the single source of truth for the field's current value.\n\n```jsx\n\u002F\u002F Parent owns the state → input is controlled\nfunction Form() {\n  const [email, setEmail] = useState('')\n  return (\n    \u003Cinput\n      value={email}                         \u002F\u002F controlled\n      onChange={e => setEmail(e.target.value)}\n    \u002F>\n  )\n}\n```\n\nWithout lifting, each input would manage its own uncontrolled state\n(the DOM), making it harder for the parent to read or validate values.\n\n**Rule of thumb:** Forms almost always need lifted, controlled state so\nthe parent can validate and submit the combined field values.\n",{"id":54,"difficulty":42,"q":55,"a":56},"lifting-state-derived","What is derived state, and how does it relate to lifting?","**Derived state** is any value you can compute from existing state or\nprops. It should **not** be duplicated in a second `useState` — compute\nit inline during render instead.\n\n```jsx\n\u002F\u002F ❌ Redundant state — fullName can diverge\nconst [firstName, setFirstName] = useState('')\nconst [lastName, setLastName] = useState('')\nconst [fullName, setFullName] = useState('')   \u002F\u002F duplicated!\n\n\u002F\u002F ✅ Derive it — always in sync, zero extra state\nconst fullName = `${firstName} ${lastName}`\n```\n\nLifting state and avoiding derived copies are two sides of the same\ncoin: both enforce a **single source of truth**.\n\n**Rule of thumb:** If a value can be computed from existing state, don't\nstore it separately — just compute it.\n",{"id":58,"difficulty":42,"q":59,"a":60},"lifting-state-performance","How can you prevent child re-renders caused by callback props passed from a parent?","Wrap the callback in `useCallback` so the function reference is stable\nbetween renders. Pair it with `React.memo` on the child to skip\nre-renders when neither value nor callback changed.\n\n```jsx\nfunction Parent() {\n  const [count, setCount] = useState(0)\n\n  const handleChange = useCallback((val) => {\n    setCount(val)\n  }, [])                  \u002F\u002F stable reference\n\n  return \u003CExpensiveChild onChange={handleChange} count={count} \u002F>\n}\n\nconst ExpensiveChild = React.memo(({ count, onChange }) => {\n  \u002F\u002F only re-renders when count or onChange actually changes\n  return \u003Cinput value={count} onChange={e => onChange(Number(e.target.value))} \u002F>\n})\n```\n\nWithout `useCallback`, a new function is created every render, breaking\n`React.memo`'s shallow equality check.\n\n**Rule of thumb:** `useCallback` is most valuable when the callback is\npassed to a memoized child — otherwise it's premature optimisation.\n",{"id":62,"difficulty":42,"q":63,"a":64},"lifting-state-multiple","When multiple pieces of related state need to be lifted, should they be separate useState calls or one object?","Group them in **one object** (or `useReducer`) when they change together\nor are conceptually coupled. Keep them separate when they change\nindependently.\n\n```jsx\n\u002F\u002F ✅ Coupled — lift as one object\nconst [position, setPosition] = useState({ x: 0, y: 0 })\n\u002F\u002F Update: setPosition(prev => ({ ...prev, x: newX }))\n\n\u002F\u002F ✅ Independent — separate calls\nconst [isOpen, setIsOpen] = useState(false)\nconst [query, setQuery]   = useState('')\n```\n\nGrouping reduces the number of props you thread through the tree;\nkeeping them separate avoids unnecessary object spread on every update.\n\n**Rule of thumb:** If you always update both values at the same time,\ngroup them; if you update them independently, keep them separate.\n",{"id":66,"difficulty":42,"q":67,"a":68},"lifting-state-initialiser","How do you initialise lifted state from a prop only once without re-syncing on every render?","Pass the initial value as the `useState` initialiser argument. React\nonly evaluates it on the **first render** — subsequent prop changes are\nintentionally ignored because the state is now owned by the parent.\n\n```jsx\n\u002F\u002F ❌ Anti-pattern: syncing prop to state on every render\nfunction Child({ initialCount }) {\n  const [count, setCount] = useState(initialCount)\n  useEffect(() => { setCount(initialCount) }, [initialCount])\n  \u002F\u002F now state and prop fight for ownership\n}\n\n\u002F\u002F ✅ Use initialValue convention — document that updates won't flow down\nfunction Counter({ initialCount }) {\n  const [count, setCount] = useState(initialCount)  \u002F\u002F first render only\n  return \u003Cbutton onClick={() => setCount(c => c + 1)}>{count}\u003C\u002Fbutton>\n}\n```\n\nIf ongoing sync is required, the parent should own the state and pass a\ncontrolled value + callback instead.\n\n**Rule of thumb:** Prefix props that seed state only once with\n`initial` (`initialCount`, `initialValue`) to signal they're not\nkept in sync.\n",{"id":70,"difficulty":14,"q":71,"a":72},"lifting-state-handler-naming","What naming convention is commonly used for event-handler props when lifting state?","Use `onX` for props that accept a callback and `handleX` for the\nfunction itself — mirroring React's own `onClick`, `onChange`, etc.\n\n```jsx\n\u002F\u002F Parent — owns state, names the prop onSearch\nfunction SearchPage() {\n  const [query, setQuery] = useState('')\n  function handleSearch(q) { setQuery(q) }\n\n  return \u003CSearchBar onSearch={handleSearch} \u002F>\n}\n\n\u002F\u002F Child — receives the prop, calls it when the user acts\nfunction SearchBar({ onSearch }) {\n  return \u003Cinput onChange={e => onSearch(e.target.value)} \u002F>\n}\n```\n\nConsistent naming makes it immediately obvious at the call site which\nprop triggers parent behaviour.\n\n**Rule of thumb:** `on` prefix = prop the caller provides;\n`handle` prefix = the local function that implements it.\n",{"id":74,"difficulty":14,"q":75,"a":76},"lifting-state-unidirectional","What is \"unidirectional data flow\" and why does React enforce it?","**Unidirectional data flow** means data travels in one direction only:\n**down** the tree via props. A child can signal a change (via callback),\nbut the parent decides whether and how state updates, then the new value\nflows back down.\n\n```\nParent state\n     ↓ props\n  Child A     Child B\n     ↑ callback (event)\n```\n\nThis makes the state transitions explicit, predictable, and easy to\ndebug — you always know where a piece of data lives and who can change\nit.\n\n**Rule of thumb:** If you find yourself needing to pass data *upward*\nwithout a callback, that's a sign the state should live higher or in\na shared store.\n",{"id":78,"difficulty":42,"q":79,"a":80},"lifting-state-anti-pattern-sync","Why is copying parent state into child state considered an anti-pattern?","When you copy a prop into `useState`, you create two sources of truth.\nIf the parent updates the prop, the child's copy stays stale unless you\nadd a `useEffect` to sync — which introduces complexity and can cause\none-render lag bugs.\n\n```jsx\n\u002F\u002F ❌ Derived state anti-pattern\nfunction Child({ value }) {\n  const [localValue, setLocalValue] = useState(value)\n  \u002F\u002F If parent changes `value`, localValue is now stale\n  useEffect(() => setLocalValue(value), [value]) \u002F\u002F band-aid\n}\n\n\u002F\u002F ✅ Just use the prop directly\nfunction Child({ value, onChange }) {\n  return \u003Cinput value={value} onChange={e => onChange(e.target.value)} \u002F>\n}\n```\n\nThe key insight: if a component should be driven by the parent, make\nit **fully controlled** — no local copy.\n\n**Rule of thumb:** Before writing `useState(someProp)`, ask whether the\nchild really needs to own that state. Usually the answer is no.\n",{"id":82,"difficulty":42,"q":83,"a":84},"lifting-state-form-submit","How does lifting state enable a parent to collect and submit form field values?","With lifted state, the parent holds every field value and can read them\nall in the submit handler without querying the DOM.\n\n```jsx\nfunction SignupForm() {\n  const [form, setForm] = useState({ name: '', email: '' })\n\n  function handleChange(field, value) {\n    setForm(prev => ({ ...prev, [field]: value }))\n  }\n\n  function handleSubmit(e) {\n    e.preventDefault()\n    api.signup(form)   \u002F\u002F all values available here\n  }\n\n  return (\n    \u003Cform onSubmit={handleSubmit}>\n      \u003CTextInput label=\"Name\"  value={form.name}  onChange={v => handleChange('name', v)} \u002F>\n      \u003CTextInput label=\"Email\" value={form.email} onChange={v => handleChange('email', v)} \u002F>\n      \u003Cbutton type=\"submit\">Sign up\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}\n```\n\n**Rule of thumb:** Lift all form fields into the parent that owns the\nsubmit action so validation and submission see a consistent snapshot.\n",{"id":86,"difficulty":87,"q":88,"a":89},"lifting-state-list-items","hard","How do you handle lifted state for a dynamic list where each item has its own editable fields?","Store the list in the parent as an array of objects. Pass each item and\nan `onChange` callback that identifies the item by index or `id`.\n\n```jsx\nfunction TodoList() {\n  const [todos, setTodos] = useState([\n    { id: 1, text: 'Buy milk', done: false },\n    { id: 2, text: 'Walk dog', done: false },\n  ])\n\n  function handleToggle(id) {\n    setTodos(prev =>\n      prev.map(t => t.id === id ? { ...t, done: !t.done } : t)\n    )\n  }\n\n  return todos.map(todo => (\n    \u003CTodoItem key={todo.id} todo={todo} onToggle={handleToggle} \u002F>\n  ))\n}\n\nfunction TodoItem({ todo, onToggle }) {\n  return (\n    \u003Clabel>\n      \u003Cinput type=\"checkbox\" checked={todo.done}\n             onChange={() => onToggle(todo.id)} \u002F>\n      {todo.text}\n    \u003C\u002Flabel>\n  )\n}\n```\n\nThe parent is the single source of truth for the entire list; each\nchild is a controlled component.\n\n**Rule of thumb:** Always identify list items by a stable `id` (not\nindex) when items can be added, removed, or reordered.\n",16,null,{"description":11},"React lifting state interview questions — single source of truth, callback props, sibling communication, when to lift vs. use a store.","react\u002Fstate-and-data-flow\u002Flifting-state","Lifting State Up","State and Data Flow","state-and-data-flow","2026-06-24","crdExT6WcsvjkdZN2V3ZRKAnaBpWm4wpjIfDlOEHUss",[101,102,105,109],{"subtopic":95,"path":22,"order":21},{"subtopic":103,"path":104,"order":12},"Context API","\u002Freact\u002Fstate-and-data-flow\u002Fcontext-api",{"subtopic":106,"path":107,"order":108},"Controlled vs Uncontrolled Components","\u002Freact\u002Fstate-and-data-flow\u002Fcontrolled-vs-uncontrolled",3,{"subtopic":110,"path":111,"order":112},"Prop Drilling and Composition","\u002Freact\u002Fstate-and-data-flow\u002Fprop-drilling-composition",4,{"path":114,"title":115},"\u002Fblog\u002Freact-lifting-state-guide","Lifting State Up in React — A Complete Guide",1782244100903]