react 死循环

我以为如下代码会死循环 :

import { FC, useEffect, useState } from 'react';

interface HeadFormProps {
  value: string;
  onChange: (values: string) => void;
}
const HeadForm: FC<HeadFormProps> = (props) => {
  const [state, _setState] = useState<string>('heh');

  const [motto, setMotto] = useState('');

  // 设置子组件状态,要同步到父组件
  const setState = (value: string) => {
    _setState(value);
    if (props.onChange) {
      props.onChange(value);
    }
  };

  // 监听父组件传入值的变更,同步至子组件
  useEffect(() => {
    if (props.value) {
      _setState(props.value);
    }
  }, [props.value]);

  return (
    <div className="head-form">
      <div className="form-groups">
        <div className="form-group flex gap-1">
          <label htmlFor="name">姓名</label>
          <input className="border" type="text" value={state} onChange={(e) => setState(e.target.value)} />
        </div>


        <div className="form-group flex gap-1 mt-2">
          <label htmlFor="name">签名</label>
          <input className="border" type="text" value={motto} onChange={(e) => setMotto(e.target.value)} />
        </div>
      </div>
    </div>
  );
};

const DemoTwo = () => {
  const [searForm, setSearForm] = useState<string>('xixi');
  return (
    <div className="wrapper">
      <HeadForm value={searForm} onChange={setSearForm} />
      <div>{JSON.stringify(searForm)}</div>
    </div>
  );
};

export default DemoTwo;

但实际上却没有!

代码分析

useEffect(() => {
  if (props.value) {
    _setState(props.value);  // 这里设置子组件的state
  }
}, [props.value]); // 依赖props.value

为什么没有死循环

  1. React的状态更新机制

    • 当您在子组件中调用setState(e.target.value)时,确实会触发父组件的onChange
    • 父组件的setSearForm会更新状态,导致父组件重新渲染
    • 父组件重新渲染时,会传递新的value给子组件
  2. 但不会死循环的原因

    • React会对状态更新进行批处理,不会立即触发渲染
    • _setState(props.value)被调用时,如果props.value与当前state相同,React会跳过重新渲染
    • 基本类型的值比较是值比较,只有当值真正改变时才会触发更新
  3. 具体流程

    • 用户输入 → 子组件setState → 触发onChange → 父组件setSearForm
    • 父组件渲染 → 传递新的value给子组件
    • 子组件useEffect检测到props.value变化 → 但_setState()发现值相同 → 跳过更新

可能产生死循环的情况

只有在特定情况下才会产生死循环:

useEffect(() => {
  // 如果这里总是设置一个不同的值
  _setState(props.value + " "); // 每次添加空格,值总是不同
}, [props.value]); // 这样会导致无限循环

您的代码没有死循环是因为React的优化机制和值比较策略阻止了不必要的渲染。

也就是说如果传入的值和设置的值相等,则不会引起再次 render!

  const [count, setCount] = useState<number>(1);

  useEffect(()=>{
    setCount(1);
    console.log('执行了');
  },[count])

如果不一样,则会

 const [count, setCount] = useState<number>(1);

  useEffect(()=>{
    setCount(count+1);
    console.log('执行了');
  },[count])

在react同一次渲染周期中,多次设置值则会被合并为一次 render 更新

让我解释什么情况属于"同一次渲染周期"。

同步代码块中的多次状态更新

const Example = () => {
  const [count, setCount] = useState(0);
  
  const handleClick = () => {
    // 这些都属于同一次渲染周期
    setCount(1);  // 第一次更新
    setCount(2);  // 第二次更新  
    setCount(3);  // 第三次更新
    // React会批量处理,最终只使用最后一次setCount(3)
  };
  
  return <button onClick={handleClick}>Count: {count}</button>;
};

您的代码具体分析

以下代码不属于同一周期,但是依然被优化了
先处理批次1:子组件使用新state("a")重新渲染
再处理批次2:父组件使用新state("a")重新渲染 → 传递新的props.value = "a"
跨组件的状态更新会产生多个渲染周期

_setState("a")和props.onChange("a")属于同一次事件循环但不同的渲染批次
React会批量处理同步代码中的多个setState,但跨组件的状态更新会产生多个渲染周期
最后的_setState("a")因为值相同而被React优化掉,避免了无限循环

const setState = (value: string) => {
  _setState(value);                    // 状态更新1
  if (props.onChange) {
    props.onChange(value);             // 触发父组件状态更新
  }
};

// 在输入框onChange中:
onChange={(e) => setState(e.target.value)}
  1. 用户输入"a"setState("a")_setState("a") + props.onChange("a")
  2. 父组件处理:父组件setSearForm("a"),安排重新渲染
  3. 父组件渲染:传递新的value="a"给子组件
  4. 子组件useEffect:检测到props.value从""变为"a"
  5. 子组件_setState("a"):但当前state已经是"a",React跳过更新

产生死循环的例外情况

只有在每次设置不同值时才会循环:

useEffect(() => {
  // 错误示例:总是设置不同的值
  _setState(props.value + Math.random()); // 每次值都不同
}, [props.value]);

您的代码能正常工作,正是因为React的智能批处理和相同值检测机制。

React不仅在同一渲染周期内会优化相同值的状态更新,在跨渲染周期的情况下也会进行优化

// 子组件内部state已经是"a"
useEffect(() => {
  if (props.value) {          // props.value = "a" 
    _setState(props.value);   // 尝试设置state为"a"(与当前值相同)
  }
}, [props.value]); // props.value从""变为"a"

// React比较发现:currentState("a") === nextState("a")
// ✅ 跳过此次状态更新,避免不必要的重新渲染

React的优化策略
同一周期内:批量处理多个setState,只使用最后一个值.
跨周期:比较新旧值,如果相同则跳过渲染.
深度优化:即使是不相关的渲染触发,也会进行值比较.

React setState 的内部行为
当新旧值相等时,setState 不会执行任何有效工作,直接返回。
这才是不会触发 render 的核心吧,虽然 setXX 执行了,可是内部代码却跳出更新,因为值相等!

600

posted @ 2025-08-28 19:00  丁少华  阅读(10)  评论(0)    收藏  举报