【问题标题】:Variable in Next.JS project has changed its value without assignmentNext.JS 项目中的变量在没有赋值的情况下改变了它的值
【发布时间】:2021-10-27 01:48:41
【问题描述】:

我遇到了一些非常奇怪的行为。我有一个更大的项目,但出于演示目的,我认为以下简单的 Next.JS 应用程序就足够了。

重现步骤:

  1. 创建一个简单的 Next.JS 项目:

    npx create-next-app my-next-app --ts

  2. 将以下2个文件添加到项目中:

// ./componens/logs.ts

export function createLogger(options: { name: string }) {
  const logIdObj = { logId: 0 };
  function vivify(o: { logId: number }) {
    let n = 0;
    Object.defineProperty(o, 'logId', {
      get: () => {
        console.warn(`Getting logId for logger ${options.name}: ${n}`);
        return n;
      },
      set: (v: number) => {
        console.warn(`Setting logId for logger ${options.name}: ${v}`);
        n = v;
      },
    });
  }
  vivify(logIdObj);

  return (message: string) => {
    logIdObj.logId += 1;
    console.log(`${logIdObj.logId}. ${message}`);
  };
}
// ./componens/Chat.tsx

import { useEffect } from 'react';
import { createLogger } from './logs';

const log = createLogger({ name: 'Chat' });

export default function Chat(): JSX.Element {
  log('render');

  useEffect(() => {
    log('useEffect');
  }, []);

  return <div>Some content</div>;
}
  1. 修改主页文件./pages/index.tsx如下:
import type { NextPage } from 'next'
import Chat from '../components/Chat'

const Home: NextPage = () => {
  return <>
      <Chat />
      <h1>Welcome</h1>
    </>
}

export default Home
  1. 在开发模式下运行项目: npm run dev

  2. 打开浏览器,导航到localhost:3000,看看你会在控制台中看到什么:

正如您在./componens/logs.ts 中看到的,对logIdObj 对象的logId 属性的访问被有意包装在getter-setter 拦截器中以记录对它的任何类型的访问,但根据日志值已更改而无需被访问了。

这是我的环境:

Distributor ID: Debian
Description:    Debian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
node: v14.17.0
npm: 7.22.0

还有我的package.json

{
  "name": "my-next-app",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "11.1.2",
    "react": "17.0.2",
    "react-dom": "17.0.2"
  },
  "devDependencies": {
    "@types/react": "17.0.30",
    "eslint": "7.32.0",
    "eslint-config-next": "11.1.2",
    "typescript": "4.4.4"
  }
}

我猜它可能与内部 Next.JS 逻辑有某种关系,但无论如何它不应该像这样工作。

一如既往,我们将不胜感激。


更新

虽然第一次回复(感谢Ben)没有明确回答问题的原因,但它提供了可能出现问题的线索。所以我修改了./componens/logs.ts这样的:

const allLogs = [] as { message: string, logId: number, time: number }[];
if (typeof window !== 'undefined') {
  // @ts-ignore
  window.allLogs = allLogs;
}

export function createLogger(options: { name: string }) {
  let logId = 0;

  return (message: string) => {
    logId += 1;
    allLogs.push({ message, logId, time: Date.now() });
    console.log(`${logId}. ${message}`);
  };
}

现在我的结果如下所示:

现在我们可以清楚地看到,渲染实际上发生了两次,console.log() 并不总是产生实际的日志。不过,目前尚不清楚原因。

有什么解释吗?

【问题讨论】:

  • 你在开发环境吗?您是否启用了StrictMode
  • 抱歉,我将 typescript 的 strict 模式与 react 的 StrictMode 混淆了。实际上,这是一个正确的线索
  • 我很高兴它成功了。

标签: javascript reactjs typescript next.js


【解决方案1】:

实际原因是 StrictMode 中 React 的特定行为:

从 React 17 开始,React 会自动修改 console.log() 等控制台方法,以在第二次调用生命周期函数时使日志静音。但是,在某些情况下,它可能会导致不良行为...

更多解释见https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects

【讨论】:

    【解决方案2】:

    你是对的。如果你改变:

    set: (v: number) => {        
      console.log(`Setting logId for logger ${options.name}: ${v}`);
      n = v;
    },
    

    set: (v: number) => {        
      alert(`Setting logId for logger ${options.name}: ${v}`);
      n = v;
    },
    

    你会看到 setter 当然被调用了。

    对于根本原因,我没有答案 - 这可能是一个值得在 Next repo 中提出作为问题的错误,或者至少有人能够解释原因。

    【讨论】:

    • 我想到了。所以这次我会这样做
    猜你喜欢
    • 2013-08-04
    • 2021-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 2019-07-14
    相关资源
    最近更新 更多