reactjs,没有无限循环,怎么来的?

Posted

技术标签:

【中文标题】reactjs,没有无限循环,怎么来的?【英文标题】:reactjs, no infinite loop, how come? 【发布时间】:2021-10-08 10:10:12 【问题描述】:

我不明白,我想用下面的代码,当我访问 Fruit.js 页面/组件('/fruits/:slug' route)时,它应该会导致无限循环......但它没有, 为什么? (我很高兴它没有,但只是好奇为什么?)

App.js

const App = () => 
    const [dummyData, setDummyData] = useState([
         slug: 'apple', name: 'Apple', color: 'Red' ,
         slug: 'watermelon', name: 'Watermelon', color: 'Green' ,
         slug: 'peach', name: 'Peach', color: 'Pink' ,
         slug: 'banana', name: 'Banana', color: 'Yellow' ,
    ]);

    const [fruitSlug, setFruitSlug] = useState('');

    const getSlug = (slug) => 
        setFruitSlug(slug);
        console.log(slug);
    ;

    return (
        <div className='App'>
            dummyData.map((fruit) => (
                <span className='fruit-link' key=fruit.slug>
                    <Link to=`/fruits/$fruit.slug`>fruit.name</Link>' '
                </span>
            ))

            <Switch>
                <Route path='/' exact component=Home />
                <Route
                    path='/fruits/:slug'
                    render=(props) => <Fruit ...props getSlug=getSlug />
                />
            </Switch>
        </div>
    );
;

export default App;

Fruit.js

const Fruit = ( getSlug ) => 
    const  slug  = useParams();

    useEffect(() => 
        getSlug(slug);
    , [getSlug, slug]);

    return (
        <div className='fruit'>
            <h1>Fruit page</h1>
        </div>
    );
;

export default Fruit;

【问题讨论】:

了解您认为应该存在无限循环的原因会很有用 “它应该会导致无限循环” 是什么让你这么认为? 我不确定我是否正确,但我认为它会导致无限循环的原因是:在访问 Fruit.js 页面/组件后,它会执行getSlug(slug) 的初始渲染getSlug(slug) 函数运行 => 将新的 slug 值设置为 fruitSlug 状态 => App.js 重新渲染 => getSlug(slug) 函数被重新创建并传递给 Fruit.js 页面/组件 => useEffect 在 Fruit 中。 js 检测到getSlug 函数不同,触发useEffect 并再次执行getSlug(slug),并重复?抱歉,这里是 reactjs 初学者。 我知道,如果我在 App.js 中的getSlug 函数中获取 api 调用并将 api 响应数据设置为状态(例如:setData(post)),我将得到无限循环,但是我上面的代码怎么没有无限循环......??所以如果这是在getSlug函数中,它会导致无限循环:fetch(postUrl).then((response) =&gt; response.json()).then((post) =&gt;setData(post)) 【参考方案1】:

让我首先给出两个类似的例子,消除您当前问题中存在的一些问题开销。

您想知道为什么下面 sn-p 中的 useEffect 回调不会触发无限重新渲染。

const  useState, useEffect  = React;

function Example() 
  const [a, setA] = React.useState("");
  const [b, setB] = React.useState(a);
  
  useEffect(() => 
    setB(a);
  ); // <- without dependencies triggers every render
    
  return (
    <div>
      <input value=a onChange=e => setA(e.target.value) />
      <p>b</p>
    </div>
  );


ReactDOM.render(<Example />, document.querySelector("#root"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

虽然当您从例如 API 传递更复杂的对象时,它似乎确实会这样做。

const  useState, useEffect  = React;

function Example() 
  const [a, setA] = React.useState("");
  const [b, setB] = React.useState( value: a );
  
  useEffect(() => 
    setB( value: a );
  ); // <- without dependencies triggers every render
    
  return (
    <div>
      <input value=a onChange=e => setA(e.target.value) />
      <p>b.value</p>
    </div>
  );


ReactDOM.render(<Example />, document.querySelector("#root"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

这是因为useState 有一个"bail out" mechanism 这个简单的事实:

退出状态更新

如果您将状态挂钩更新为与当前状态相同的值, React 将在不渲染子元素或触发效果的情况下退出。 (React 使用 Object.is comparison algorithm。)

请注意,React 可能仍需要再次渲染该特定组件 在保释之前。这不应该是一个问题,因为 React 不会 不必要地“深入”到树中。如果你做的很贵 渲染时计算,您可以使用 useMemo 优化它们。

第一个示例将无限触发重新渲染,如果它不是这种救助机制。当 React 尝试设置与当前状态相同的状态时,它会停止。

我们经常从 API 接收 JSON,在解析时给我们一个复杂的对象。两个对象永远不会相等,即使它们包含相同的内容。

const a =  name: "John Doe" ;
const b =  name: "John Doe" ;
const c = a;

Object.is(a, b) //=> false
Object.is(a, c) //=> true

在上面的例子中,虽然ab 包含相同的内容,但它们不是同一个对象,因此彼此不相等。而ac 都是同一个对象,它们只是引用同一个对象的两个不同标签。如果你在哪里改变ac 也会改变。因此它们被认为是平等的。

解析 JSON 总是构建新对象,因此结果永远不会相等(除非 JSON 描述了原始数据类型)。

const json = ' "name": "John Doe" ';
const a = JSON.parse(json);
const b = JSON.parse(json);

Object.is(a, b) //=> false

【讨论】:

所以基本上我的代码,它没有给我任何幕后无限循环的原因是因为 reactjs 的“纾困”机制,所以当我访问 Fruit.js 页面/组件,useEffect 触发和getSlug(slug) 函数执行并将新值设置为fruitSlug 状态,这导致App.js 重新渲染,然后再次触发Fruit.js 中的useEffect 并执行getSlug(slug) 函数再次但在执行getSlug 函数时,reactjs 发现我要设置为fruitSlug 状态的新值是 与当前值相同,因此它没有将新值设置为fruitSlug 状态,App.js 不会重新渲染,所以一切都停止了......我我说对了吗? @JustANewCoder 是的,你理解正确。

以上是关于reactjs,没有无限循环,怎么来的?的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS fetch 导致无限循环

为啥我在 React JS 中的代码会进入满足这两个条件的无限循环?

android两个animation无限循环怎么做?

REST API 无限循环

win10自动修复失败无限循环是怎么回事?

Java - 带有浮点数的无限while循环[重复]