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) => response.json()).then((post) =>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
在上面的例子中,虽然a
和b
包含相同的内容,但它们不是同一个对象,因此彼此不相等。而a
和c
都是同一个对象,它们只是引用同一个对象的两个不同标签。如果你在哪里改变a
,c
也会改变。因此它们被认为是平等的。
解析 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,没有无限循环,怎么来的?的主要内容,如果未能解决你的问题,请参考以下文章