← React Course

REACT CORE · 97

Props / State / Re-render:誰擁有資料?

React 最重要的不是 useState 這個 API,而是 state ownership。哪個 component 擁有 source of truth,決定了資料怎麼流、誰能修改、誰會 re-render。

Learning outcomes

1. Props:parent 傳給 child 的 input

function CourseCard({
  course
}) {
  return (
    <h2>
      {course.title}
    </h2>
  );
}

function App() {
  return (
    <CourseCard
      course={course}
    />
  );
}

child 不應把 props 當自己的可任意修改 local state。

2. State:component 需要跨 render 保存的資料

const [
  courses,
  setCourses
] = useState([]);

當 state setter 被呼叫,React 會排程更新,之後重新執行 component function 來計算新 UI。

event→ setStateschedule update→ rendernew UI

3. State 是某一次 render 的 snapshot

const [
  count,
  setCount
] = useState(0);

function handleClick() {
  setCount(count + 1);
  setCount(count + 1);
}

這兩行都讀到目前這次 render 的同一個 count snapshot;不要假設第一個 setter 立刻同步改變 local variable。

4. Functional update:依賴前一個 state 時更穩

setCount(c => c + 1);
setCount(c => c + 1);

每個 updater 會基於 queue 中前一個結果計算下一個值。

5. Immutable update

setCourses(current =>
  current.map(course =>
    course.id === id
      ? {
          ...course,
          score: nextScore
        }
      : course
  )
);

不要:

courses[0].score = 100;
setCourses(courses);

這保留相同 array reference,又直接 mutate nested object,容易破壞 React 對 state change 的推理。

6. State ownership:提升到最近共同 parent

App
├─ Filters
└─ CourseList

如果 Filters 要改變 CourseList 顯示結果,filter state 最自然通常放在兩者最近共同 parent,透過 props 往下傳。

Project checkpoint:Course Dashboard v2

function App() {
  const [
    courses,
    setCourses
  ] = useState(initialCourses);

  function updateScore(
    id,
    nextScore
  ) {
    setCourses(current =>
      current.map(course =>
        course.id === id
          ? {
              ...course,
              score: nextScore
            }
          : course
      )
    );
  }

  return (
    <CourseList
      courses={courses}
      onScoreChange={
        updateScore
      }
    />
  );
}

App 是 source of truth;CourseList / CourseCard 收資料與 callback。

Debug evidence:state 為什麼「沒更新」?

  1. handler 有沒有真的執行?
  2. setter 有沒有被呼叫?
  3. new state reference / value 是否真的改變?
  4. UI 讀的是不是同一個 source of truth?
  5. 是不是在同一次 render 裡誤讀舊 snapshot?

Knowledge check

  1. props 與 state 的 ownership 差在哪?
  2. 為什麼連續兩次 setCount(count + 1) 不一定 +2?
  3. 把一個 mutating array update 改成 immutable update。
  4. 給你 Filters + CourseList,說明 filter state 應放哪裡。