-3

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

应用程序.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;
4

1 回答 1

1

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

您想知道为什么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这是由于具有“纾困”机制的简单事实:

摆脱状态更新

如果您将 State Hook 更新为与当前状态相同的值,React 将退出而不渲染子级或触发效果。(React 使用Object.is比较算法。)

请注意,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
于 2021-08-03T10:51:31.390 回答