وب مدرن، رابط کاربری و تجربه‌ی وب · فصل ۴ — React، State و Visual Regression · 45

React Fundamentals and Component State

Model props, local state, derived values, shared ownership and component identity explicitly; implement the smallest state model and prove preservation/reset behavior with interaction and rerender evidence rather than visual appearance alone. می‌تواند «React Fundamentals و Component State» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

React Fundamentals و Component State — fa-IR

Lesson summary

Model props, local state, derived values, shared ownership and component identity explicitly; implement the smallest state model and prove preservation/reset behavior with interaction and rerender evidence rather than visual appearance alone.

می‌تواند «React Fundamentals و Component State» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

This lesson produces

می‌تواند «React Fundamentals و Component State» را در یک feature محدود طراحی/پیاده‌سازی کند و correctness، accessibility یا failure behavior مرتبط را اثبات کند.

Who it is for

Foundation

Worked baseline, explicit evidence table, safe local fixture and one guided failure probe.

Repair

Reduce to one failing contract, observe evidence, repair, then restore full case.

Experienced

Skip syntax walkthrough; require independent implementation, mutation evidence and residual-risk statement.

Prerequisites

A correct-looking editor can still have the wrong state model

Imagine a two-pane profile editor. The left pane filters and selects a profile. The right pane edits a draft for the selected profile. The product requirement sounds simple: selection must be shared across both panes, the filter text should survive when the selected profile changes, and the draft should reset when the profile identity changes. A visually convincing implementation can still violate all three rules.

The common failure is not missing syntax. It is an incorrect model of what must be remembered, who owns it, what can be derived, and which component identity should preserve or reset local state. A child can copy a prop into local state and silently stop following later prop changes. Two siblings can each keep their own selectedId and disagree. A summary can be duplicated into state and drift from the draft that supposedly defines it. An Effect can be added to “synchronize” values that were already available during render, producing extra intermediate states instead of a stronger model.

This lesson owns that boundary. Your completion artifact is React Component State Boundary Record v1. It must trace each requirement to a source of truth, an owner component, props/state/derived data, the relevant render-tree identity, observed interaction behavior, a deliberate failure mutation, the repair, a changed-context transfer, and one residual limitation. A screenshot is not enough because the bug often appears only after a sequence of interactions.

Scope, route, and evidence bar

WEB-001 is the hard prerequisite: reuse its bounded component/DOM evidence when the diagnostic path needs it. This lesson owns local component state, state shape, shared ownership, intentional preserve/reset behavior, and the boundary between render logic and Effects. It does not own server-state/query lifecycle or typed routing (WEB-012), visual-regression/catalog concerns (WEB-018), or build/assets/splitting (WEB-014).

Foundation route: keep the ownership-and-identity matrix beside the code and name one source of truth at a time. Repair route: isolate one misconception and run the smallest mutation that exposes it. Experienced route: skip the introductory useState tour, but still prove the ownership boundary, derived-data decision, identity reset, and changed-context transfer. The evidence bar is identical for every route.

Separate observation from inference. “After selecting profile B, the editor input contains B's original name” is an observation. “Therefore props always reset state” is false inference. “The filter text remains ali after profile selection changes” is an observation. “Therefore all local state should always be preserved” is overgeneralization.

Mental model: essential memory → owner → render → identity → evidence

Use this causal chain:

requirement → essential source of truth → closest responsible owner → props/local state/derived value → render-tree identity → interaction sequence → evidence.

React describes state as component-specific memory retained between renders. That does not mean every value used by a component belongs in state. A value that can be calculated from current props and state during render is usually a derived value, not a second source of truth. [S-REACT-STATE] [S-REACT-STRUCTURE]

When two components must coordinate the same fact, move that fact to their closest common owner and pass data plus event handlers down. The important point is not “put everything at the top”; it is “one shared fact, one authoritative owner at the nearest level that actually needs coordination.” [S-REACT-SHARING]

State preservation is also not attached to the JSX spelling of a component. React associates state with component identity in the render tree. Position, component type, and key influence whether React preserves or resets that state. This is why a draft can intentionally reset when the selected profile changes even though the editor component is rendered in the same place. [S-REACT-IDENTITY]

Foundation: five contracts that must remain distinct

1. Local-state contract

Use local state for information the UI must remember between renders and that the component legitimately owns. Filter text is a good example in this scenario: changing the selected profile should not erase the filter just because a different profile is open. The filter is independent UI memory.

2. State-shape contract

Store the minimum essential facts. If the draft has firstName and lastName, then fullName can usually be derived during render:

const fullName = `${draft.firstName} ${draft.lastName}`.trim()

Creating fullName as another state variable creates synchronization work and the possibility of contradiction. React's current guidance explicitly recommends avoiding redundant and duplicated state when the value can be calculated from current props/state. [S-REACT-STRUCTURE]

3. Shared-ownership contract

If both panes need the selected profile, they should not maintain independent selection state. Put selectedId in their closest common owner, for example ProfileWorkspace, then pass the selected value and callbacks down. [S-REACT-SHARING]

4. Component-identity contract

The draft is local editor state, but the requirement says that draft belongs to a profile identity. If switching from A to B must start B with a fresh draft, make that identity boundary explicit:

<ProfileEditor key={selectedProfile.id} profile={selectedProfile} />

The key is not a magic “refresh” button. It is a declaration that the editor instance for A and the editor instance for B are different identities for this requirement. [S-REACT-IDENTITY]

5. Effect-boundary contract

Effects synchronize React with external systems. They are not the default way to compute render data or respond to ordinary user events. If fullName is available from current draft fields, calculate it during render. If clicking Save should call a handler, handle it in the event. Do not create an Effect merely to copy one React value into another. [S-REACT-EFFECT]

Deep explanation: why plausible shortcuts create hidden bugs

Consider mirroring a prop into state:

function ProfileEditor({ profile }) {
  const [name, setName] = useState(profile.name)
  // ...
}

The initializer is used for that component state's initial value. If the same component identity later receives a different profile, React does not treat the new prop as an instruction to overwrite local state. That can be exactly what you want for some interfaces, but it violates this scenario when a profile change must reset the draft. The repair should follow the product identity rule, not a superstition such as “always add an Effect when props change.”

Now consider two siblings:

<ProfileList />
<ProfileDetails />

If both need the same selected profile but each keeps its own selectedId, there are two sources of truth. They can agree during a demo and diverge under a different interaction path. Lifting the selection to their common owner removes the disagreement structurally. [S-REACT-SHARING]

A redundant summary is subtler. Suppose summary is stored and updated inside an Effect whenever draft fields change. That may eventually display the expected string, but it introduces an extra update path and an intermediate render where the draft changed but the duplicated summary still represents the previous draft. If the summary is pure render data, derive it. Reserve Effects for synchronization beyond React's render calculation, such as coordinating with a genuinely external system. [S-REACT-EFFECT]

Finally, identity is not the same as data equality. A component may receive new props and still be the same component identity in the same position. Conversely, changing a key tells React to treat the subtree as a new identity and reset its local state. The engineering question is: what does the product say the local state belongs to? In this profile editor, the draft belongs to the selected profile; the filter belongs to the surrounding workspace. Therefore the draft resets on profile identity change while the filter survives.

Worked example: one workspace, one selection, one profile-scoped draft

Model the scenario with four pieces:

  1. filterText — essential local UI state owned by ProfileWorkspace because it should survive profile switches.
  2. selectedId — essential shared state owned by ProfileWorkspace because both panes depend on it.
  3. selectedProfile — derived from profiles and selectedId; do not duplicate the object in another state variable.
  4. draft — local state inside ProfileEditor, scoped to the selected profile identity.

A defensible shape is:

function ProfileWorkspace({ profiles }) {
  const [filterText, setFilterText] = useState('')
  const [selectedId, setSelectedId] = useState(profiles[0].id)
  const selectedProfile = profiles.find(p => p.id === selectedId)!

  return (
    <>
      <ProfileList
        profiles={profiles}
        filterText={filterText}
        onFilterTextChange={setFilterText}
        selectedId={selectedId}
        onSelect={setSelectedId}
      />
      <ProfileEditor
        key={selectedProfile.id}
        profile={selectedProfile}
      />
    </>
  )
}

Inside ProfileEditor, initialize a draft from the profile and derive its summary during render. The key ensures a profile identity change creates a fresh editor state. The surrounding workspace is not keyed by profile, so its filter remains intact.

Record this in WEB-011-TAB1: requirement, essential source of truth, owner, representation (props, state, or derived render value), identity boundary, interaction evidence, preserve/reset decision, mutation result, and residual limitation.

Failure probes: the evidence must react when the model breaks

Mutation A — duplicate a derived summary

Add summary state plus an Effect that updates it from the draft. Then deliberately update only one dependency or introduce a path where the copied value does not update. Predict the mismatch before running the interaction. The point is not “Effects are bad”; it is that this value did not need a second source of truth.

Mutation B — split shared selection ownership

Move selectedId into both panes. Trigger a selection from one pane and observe that the other can still represent the previous profile. The repair is to restore one shared owner, not to add a second synchronization channel.

Mutation C — remove the profile identity key

Keep the same ProfileEditor identity while changing the profile prop. Type a draft for A, select B, and observe whether A's local draft survives into B. If the requirement says B must start fresh, the evidence should fail. Restore the explicit identity boundary and repeat the same interaction.

Mutation D — mirror render data with an Effect

Replace a direct derived value with Effect-driven copied state. Inspect the update path. A passing visual frame after the Effect runs does not erase the extra intermediate state or the extra synchronization responsibility. Diagnose whether an external system actually exists; if not, derive during render. [S-REACT-EFFECT]

Experienced bridge: treat state as a data-model decision

For an experienced React developer, syntax is not the challenge. Answer five questions directly:

  1. Which facts are essential memory rather than derivable values?
  2. Which facts are shared, and what is their closest common owner?
  3. Which local state belongs to the workspace, and which belongs to a selected entity identity?
  4. Which values are calculated during render, and which genuinely require synchronization with something external?
  5. Which mutation would prove your current model wrong?

A good model reduces the number of synchronization edges. If you need several Effects whose only job is to keep React state variables aligned with other React state variables, inspect the state shape first. If two siblings repeatedly notify each other so their selections stay aligned, inspect ownership first. If a draft unexpectedly survives record changes, inspect identity first.

Alternatives and trade-offs

Local draft vs controlled draft in the parent. Keeping the draft local can be appropriate when only the editor needs in-progress values. Lifting the entire draft can be appropriate when siblings must coordinate or persist it. The lesson does not prescribe one universal level; it requires the smallest owner that satisfies the actual coordination requirement.

key reset vs explicit draft reset. A key is strong when product identity maps cleanly to component identity and you want the whole local editor subtree reset. An explicit state update can be appropriate when only part of the draft should reset or when state must be migrated. Choose the mechanism that matches the reset boundary; do not use a key as an unexplained re-render trick.

Derived render value vs memoization. “Derived” does not mean “must use useMemo.” First remove redundant state. Only then consider memoization if measurement shows a worthwhile computation cost. Performance optimization is not a substitute for correct state shape.

Effect vs event handler. If an action happens because the user clicked Save, the event handler is normally the direct place for that action. If React must stay synchronized with an external system because the component is on screen, an Effect may be appropriate. [S-REACT-EFFECT]

Practice: repair the fixture, then transfer the method

Guided repair

Open WEB011-LAB1 and inspect the broken starter before editing. Write the contract first: “selection is shared across panes; filter state survives profile changes; draft state resets with profile identity; summary is derived; no Effect is used merely to mirror render data.”

Repair only what the contract requires. Keep one selectedId in the common owner. Derive selectedProfile. Keep the filter outside the profile-keyed editor. Scope the editor draft to profile identity. Derive the summary directly from draft fields. Run the public structural checks, then execute the interaction sequence in the provided React runtime and fill the evidence record with what you actually observe.

Choose one mutation before seeing its result. Record the predicted failure, apply the mutation, observe the result, diagnose which contract dimension failed, repair it, and rerun the identical interaction sequence.

Independent transfer

The transfer fixture changes from profiles to a two-pane project editor. It still needs one shared selected project, a query/filter that survives project changes, and a project-scoped draft that resets when the selected project identity changes. Re-derive the owner and identity boundary. Do not copy profile-specific IDs or assume the same component names prove the same model.

Transfer is blocking. Reproducing the guided code without re-deriving the requirement-to-owner-to-identity chain is not sufficient evidence.

Verification: what the boundary record must prove

React Component State Boundary Record v1 contains at least:

  • tested React/runtime context and fixture identifier;
  • requirement-to-source-of-truth mapping;
  • owner component for each essential state item;
  • state vs props vs derived-render classification;
  • component identity and intended preserve/reset behavior;
  • interaction sequence and observed result;
  • mutation prediction and observation;
  • diagnosis tied to one owned dimension;
  • repair and post-repair result;
  • changed-context transfer result;
  • one residual limitation.

Browser/DOM behavior must only be claimed when actually observed. This Macro-A package does not fabricate a learner browser run. Its hidden oracle checks the reference fixture's source-level contract and verifies that deliberate mutations are detectable. The learner evidence record remains incomplete until the interaction is executed in the publisher-provided React runtime.

A screenshot of the final editor is weak evidence because duplicated state can agree temporarily. A source-code review alone is also incomplete because preserve/reset requirements are behavioral. The artifact therefore combines implementation structure with an explicit interaction sequence.

Assessment and completion

WEB-011-S1 — retrieval. Distinguish essential state, derived data, shared ownership, component identity, and the Effect boundary; state one non-goal.

WEB-011-S2 — application (blocking). Repair the profile editor and complete the boundary record with traceable ownership and interaction evidence.

WEB-011-S3 — diagnosis (blocking). Predict and run one deliberate state-shape, ownership, or identity mutation; diagnose it; repair it; and record post-repair evidence.

WEB-011-S4 — decision. Defend one choice such as local vs lifted draft ownership or key-based vs explicit reset for the bounded requirement.

WEB-011-S5 — transfer (blocking). Apply the method to the project-editor fixture without copying profile-specific assumptions.

Completion requires contract correctness, evidence quality, scope discipline, and independent transfer. A legitimate residual limitation is acceptable, for example: “verified in the provided client-rendered fixture; server-state lifecycle and routing are outside WEB-011.”

Sources and freshness boundary

The frozen Bible defines five official React source boundaries, all rechecked for this authoring pass on 2026-09-13:

  • S-REACT-STATE — state as component memory retained between renders.
  • S-REACT-STRUCTURE — avoid contradictions, redundant state, and duplication.
  • S-REACT-SHARING — lift coordinated state to the closest common owner.
  • S-REACT-IDENTITY — state preservation/reset follows render-tree identity, including type/position/key.
  • S-REACT-EFFECT — Effects are for synchronization with external systems rather than routine render derivation or ordinary event logic.

React 19.3 was released on 2026-09-09. That moving release number is review context, not a new lesson claim and not a reason to expand WEB-011 into new 19.3 APIs. The owned fundamentals above remain bound to the official learning guidance and will be reviewed again by the Bible's due date, 2026-10-17. [S-REACT-STATE] [S-REACT-STRUCTURE] [S-REACT-SHARING] [S-REACT-IDENTITY] [S-REACT-EFFECT]

The MAKNOW boundary record is an instructional evidence design, not an external React API. The lesson does not claim that every value belongs in state, that derived values need Effects, that changing props automatically resets local state, or that a correct-looking render proves the state model is correct.

Assessment

There is no answer key in this lesson pack; answer it yourself and mark honestly. "Needs review" sends the lesson to your review queue.

WEB-011-S1 retrieval

Classify essential state, derived values, shared ownership, component identity, and the Effect boundary. State one explicit non-goal.

lesson ✗AI ✗notes ✗

WEB-011-S2 application

Repair the two-pane profile editor so selection is shared, filter state persists, profile draft resets by identity, and summary is derived. Complete the boundary record.

lesson ✗AI ✗notes ✗

WEB-011-S3 diagnosis

Predict and run one state-shape, ownership, or identity mutation; diagnose the violated dimension, repair it, and rerun the same interaction.

lesson ✗AI ✗notes ✗

WEB-011-S4 decision

Defend local vs lifted draft ownership or key-based vs explicit reset for one bounded requirement.

lesson ✗AI ✗notes ✗

WEB-011-S5 transfer

Transfer the method to the project editor: shared selected project, persistent query, project-scoped draft reset. Re-derive ownership and identity.

lesson ✗AI ✗notes ✗
Assessment rubric

Critical dimensions:

  1. Contract Correctness — Implementation and explanation preserve the owned semantic/behavioral contract.
  2. Evidence Quality — Observed evidence, mutation and repair are traceable and do not rely on appearance alone.
  3. Scope Discipline — Adjacent lesson outcomes and unsupported generalizations are explicitly deferred.
  4. Transfer Independence — Changed-context task is solved without decisive scaffold.

Blocking assessments: S2, S3, S5. Non-blocking assessments still require contract and scope discipline. Browser/DOM evidence must come from learner observation; machine checks must not fabricate it.

References & freshness

Case studies

Lab files

Goal: repair the two-pane profile editor without adding another source of truth.

Required behavior:

  1. One shared selected profile across both panes.
  2. Filter text survives profile changes.
  3. Draft resets when profile identity changes.
  4. Summary is derived from current draft, not synchronized by an Effect.
  5. Record an observed interaction, one mutation/repair, and the project-editor transfer in the evidence JSON.

Run python3 run_checks.py for public structural checks. Browser/DOM claims must come from an actual run in the publisher-provided React runtime.

Submission evidence rule: each observation entry must separate observation from inference; complete mutation prediction/change/observation/diagnosis, repair, transfer, residual limitation, and attestation. The transfer is independently re-derived for projects: keep one shared selected project, preserve query text across project switches, reset the project draft at project identity change, and derive the display label instead of mirroring it into state. Do not copy profile-specific identifiers into the project solution.

Lesson media & slides

MAKNOW_WEB-011_DECISION_EVIDENCE_TABLE_v1.0.0.svg
DECISION EVIDENCE TABLE v1.0.0.svg
MAKNOW_WEB-011_FAILURE_REPAIR_v1.0.0.svg
FAILURE REPAIR v1.0.0.svg
MAKNOW_WEB-011_PRIMARY_MODEL_v1.0.0.svg
PRIMARY MODEL v1.0.0.svg
MAKNOW_WEB-011_WORKED_EXAMPLE_v1.0.0.svg
WORKED EXAMPLE v1.0.0.svg

Teaching files (download)

XLSXReact Component State Boundary Record v1 v1.0.0.xlsxTeaching files (download)
DOCXReact Component State Boundary Record v1 WORKSHEET v1.0.0.docxTeaching files (download)
PDFReact Component State Boundary Record v1 WORKSHEET v1.0.0.pdfTeaching files (download)