React 核心机制:State、Props、Effect 与组件身份

React 核心机制:State、Props、Effect 与组件身份

React 组件可以理解成一个根据输入计算界面的函数:

界面 = f(props, state)

props 是父组件提供的输入,state 是组件拥有的可变状态。状态改变后,React 会重新执行组件函数,比较新旧结果,然后把必要变化提交到真实 DOM。

State 是一次渲染的快照

考虑下面的代码:

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

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

  return ;
}

点击后结果通常是 1,而不是 2。原因是两次表达式都读取本次渲染中的同一个 count 快照,也就是 0

如果下一次状态依赖上一次结果,应使用函数更新:

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

React依次处理更新队列,第二个函数会接收到第一个函数产生的结果,因此最终得到 2

Props、State 与单向数据流

父组件管理自己的 state,并将数据作为 props 传给子组件:

function App() {
  const [name, setName] = useState("Lin");

  return (
     setName("Ming")}
    />
  );
}

子组件不能直接修改 props,但可以调用父组件传入的回调,请求父组件更新状态。

如果某个状态只有一个组件需要,就放在该组件内部;如果多个组件需要共享,就把状态放到它们最近的共同父组件中。这叫状态提升。

它的价值是让数据只有一个真实来源,使修改路径清楚,也让组件更容易复用和测试。

Effect 用来同步外部系统

组件渲染应该尽量保持为纯计算。定时器、事件监听、WebSocket 和第三方组件等外部系统,需要通过 useEffect 同步:

useEffect(() => {
  const id = setInterval(() => {
    console.log("tick");
  }, 1000);

  return () => {
    clearInterval(id);
  };
}, []);

Effect 在界面提交后执行。返回的清理函数负责撤销本次 Effect 创建的资源:

setInterval       <-> clearInterval
addEventListener  <-> removeEventListener
WebSocket connect <-> close
订阅              <-> 取消订阅

依赖变化时,React会先清理上一次 Effect,再使用新依赖建立新的 Effect。组件卸载时也会执行清理。

由现有 props 和 state 能直接计算出的值,通常不需要 Effect。Effect主要用于让 React 与外部系统保持同步。

key 表示组件身份

渲染列表时,React使用 key 判断两次渲染中的元素是否代表同一个对象:

users.map(user => (
  
));

稳定的 ID 能让 React 在列表排序后仍然识别每一项。如果使用数组下标,当项目插入、删除或换序时,组件状态可能被错误地保留到另一个项目上。

因此:

key={user.id}       // 推荐
key={index}         // 动态列表中有风险
key={Math.random()} // 每次变化,会破坏组件身份

React的核心并不是“自动修改 HTML”,而是根据 props 和 state 重新计算界面,通过 Effect 管理外部同步,再利用稳定的组件身份准确提交变化。理解这些机制后,Hooks 和组件设计就会变得更容易判断。

没有回应

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注