{"record":{"id":"ba9414ae7875da0a","repo":"facebook/react","slug":"cannot-update-optimistic-state-while-rendering","errorCode":null,"errorMessage":"Cannot update optimistic state while rendering.","messagePattern":"Cannot update optimistic state while rendering\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-reconciler/src/ReactFiberHooks.js","lineNumber":3796,"sourceCode":"      : SyncLane;\n  const update: Update<S, A> = {\n    lane: lane,\n    // After committing, the optimistic update is \"reverted\" using the same\n    // lane as the transition it's associated with.\n    revertLane: requestTransitionLane(transition),\n    gesture: null,\n    action,\n    hasEagerState: false,\n    eagerState: null,\n    next: null as any,\n  };\n\n  if (isRenderPhaseUpdate(fiber)) {\n    // When calling startTransition during render, this warns instead of\n    // throwing because throwing would be a breaking change. setOptimisticState\n    // is a new API so it's OK to throw.\n    if (throwIfDuringRender) {\n      throw new Error('Cannot update optimistic state while rendering.');\n    } else {\n      // startTransition was called during render. We don't need to do anything\n      // besides warn here because the render phase update would be overidden by\n      // the second update, anyway. We can remove this branch and make it throw\n      // in a future release.\n      if (__DEV__) {\n        console.error('Cannot call startTransition while rendering.');\n      }\n    }\n  } else {\n    const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);\n    if (root !== null) {\n      // NOTE: The optimistic update implementation assumes that the transition\n      // will never be attempted before the optimistic update. This currently\n      // holds because the optimistic update is always synchronous. If we ever\n      // change that, we'll need to account for this.\n      startUpdateTimerByLane(lane, 'setOptimistic()', fiber);\n      scheduleUpdateOnFiber(root, fiber, lane);","sourceCodeStart":3778,"sourceCodeEnd":3814,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-reconciler/src/ReactFiberHooks.js#L3778-L3814","documentation":"dispatchOptimisticState backs the setter returned by useOptimistic. If called while the owning component is rendering (isRenderPhaseUpdate(fiber) true), it throws when the throwIfDuringRender flag is set — unlike startTransition-during-render, which only warns because it predates the rule. Optimistic updates are input signals from event handlers/actions; they may never originate during render.","triggerScenarios":"Calling addOptimistic(...) (the useOptimistic setter) in the component body, in a JSX expression or default parameter evaluated during render, or from any code that runs while React is rendering that component.","commonSituations":"Trying to derive optimistic state during render; converting a state toggle into useOptimistic without moving the dispatch out of render; optimistic UI code shared between render and handlers.","solutions":["Move the addOptimistic call into an event handler, form action, or transition callback","If the goal is render-time behavior, render the merged view directly (compute base + pending during render) instead of dispatching","Initialize the optimistic hook with the right fallback and only enqueue updates on user input"],"exampleFix":"// before\nfunction List({todos}) {\n  const [shown, addOptimistic] = useOptimistic(todos);\n  if (todos.length === 0) addOptimistic(placeholder); // throws during render\n  return <ul>{shown.map(...)}</ul>;\n}\n\n// after\nfunction List({todos}) {\n  const [shown, addOptimistic] = useOptimistic(todos, appendTodo);\n  // addOptimistic is only called from onSubmit / actions / transitions\n  return <ul>{shown.map(...)}</ul>;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Render-phase throw: boundary around optimistic components\n<ErrorBoundary fallback={<PlainList/>} onError={(e) => warnDeveloper('addOptimistic called during render', e)}>\n  <OptimisticTodos />\n</ErrorBoundary>","preventionTips":["Call addOptimistic only from event handlers, form actions, or transition callbacks","Compute render-time derived state directly instead of enqueueing optimistic updates during render","Initialize useOptimistic with the correct fallback so no render-time seed call is tempting","When porting setState code, audit that every dispatch site is outside render"],"tags":["react","hooks","use-optimistic","render-phase-update"],"backgroundTag":"update-during-render","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}