微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么使用 Hooks 的 ReactJS 组件会根据开发者控制台是否打开而呈现一次或两次?

如何解决为什么使用 Hooks 的 ReactJS 组件会根据开发者控制台是否打开而呈现一次或两次?

以下代码在 codeandBox.io 网站的控制台(该版本使用 StrictMode)以及下面的代码段(使用 {{1} }):

StrictMode
const { useState,useEffect } = React;

function useCurrentTime() {
  const [timeString,setTimeString] = useState("");

  useEffect(() => {
    const intervalID = setInterval(() => {
      setTimeString(new Date().toLocaleTimeString());
    },100);
    return () => clearInterval(intervalID);
  },[]);

  return timeString;
}

function App() {
  const s = useCurrentTime();
  console.log(s);

  return <div className="App">{s}</div>;
}

ReactDOM.render(<App />,document.getElementById("root"));

演示:https://codesandbox.io/s/gallant-bas-3lq5w?file=/src/App.js(使用 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.development.js"></script>

这是一个使用生产库的片段;它仍然记录两次:

StrictMode
const { useState,document.getElementById("root"));

然而,当我打开开发者控制台时,我看到每次只打印一次,而且在 codeandBox.io 的控制台中,我看到它打印一次。

然后如果我使用 create-react-app 创建一个独立的 React 应用程序,并使用上面的代码,并且每次都打印两次。

如何理解这种行为,根据不同情况打印一次或两次?我的想法是,如果状态发生变化,那么 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> 将使用新字符串重新呈现,一次,因此它会被打印一次。特别奇怪的是为什么打印了两次,打开Dev Console就打印一次?

解决方法

据我所知,我们看到对 App 的双重调用是预期行为。来自useState documentation

退出状态更新

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

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

那里的关键是“请注意,React 可能仍需要在退出之前再次渲染该特定组件...”“...React 不会不必要地运行”更深入”到树中......”

事实上,如果我更新你的例子,让它使用一个子组件来显示时间字符串,我们只会看到子组件每个 timeString 值被调用一次,而不是两次就像 App 一样,即使子组件没有包含在 React.memo 中:

const { useState,useEffect } = React;

function useCurrentTime() {
    const [timeString,setTimeString] = useState("");
  
    useEffect(() => {
        const intervalID = setInterval(() => {
            setTimeString(new Date().toLocaleTimeString());
        },100);
        return () => clearInterval(intervalID);
    },[]);
  
    return timeString;
}

function ShowTime({timeString}) {
    console.log("ShowTime",timeString);
    return <div className="App">{timeString}</div>;
}

function App() {
    const s = useCurrentTime();
    console.log("App",s);
  
    return <ShowTime timeString={s} />;
}

ReactDOM.render(<App />,document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

当我运行它时,我看到:

App 09:57:14
ShowTime 09:57:14
App 09:57:14
App 09:57:15
ShowTime 09:57:15
App 09:57:15
App 09:57:16
ShowTime 09:57:16
App 09:57:16
App 09:57:17
ShowTime 09:57:17
App 09:57:17
App 09:57:18
ShowTime 09:57:18
App 09:57:18
App 09:57:19
ShowTime 09:57:19
App 09:57:19
App 09:57:20
ShowTime 09:57:20
App 09:57:20

请注意,尽管 App 的每个值都被调用了两次,但 timeString 只被调用了一次。

我应该注意到,这比使用 ShowTime 组件时更自动化。如果您没有实现 shouldComponentUpdate,等效的 class 组件将每秒更新 10 次。 :-)

您在 CodeSandbox 上看到的一些内容很可能是由于 class,更多关于 here。但是对于每个 StrictMode 值对 App 的两次调用只是 React 做的事情。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。