render 在 useEffect 之前调用

2024-04-09

我正在尝试使用 React 和 Leva js 创建一个简单的应用程序。

基本上有一个项目数组,每个项目都有一个名称和一个数字数组。 Leva 面板包含两个选择,用户可以从项目数组中选择两个项目。

如果两个选定的项目具有相同的长度,则可以,否则应用程序应返回错误。

这里是主要代码和这是一个工作演示 https://codesandbox.io/s/test-qg5xs7?file=/src/App.jsx.

App.jsx:

export const App = () => {
  const [haveSameNumberOfValues, setHaveSameNumberOfValues] = useState(true);
  const [showResult, setShowResult] = useState(haveSameNumberOfValues);

  const { startValuesName, endValuesName } = useControls({
    startValuesName: {
      value: VALUES[0].name,
      options: VALUES.map((d) => d.name)
    },
    endValuesName: { value: VALUES[1].name, options: VALUES.map((d) => d.name) }
  });

  useEffect(() => {
    const startValuesItem = getValuesFromName(startValuesName);
    const endValuesItem = getValuesFromName(endValuesName);
    const startValues = startValuesItem.values;
    const endValues = endValuesItem.values;

    const values = [startValues, endValues];
    const valuesLenght = values.map((path) => path.length);
    const haveValuesTheSameNumberOfItems = valuesLenght.every(
      (pathLength) => pathLength === valuesLenght[0]
    );
    setHaveSameNumberOfValues(haveValuesTheSameNumberOfItems);
    setShowResult(haveValuesTheSameNumberOfItems);
  }, [startValuesName, endValuesName]);

  console.log("\n");
  console.log("haveSameNumberOfValues:", haveSameNumberOfValues);
  console.log("showResult:", showResult);

  return (
    <div className="w-screen h-screen flex flex-col justify-center items-center">
      {!haveSameNumberOfValues && <div>:( Error.</div>}

      {showResult && (
        <Result
          startValues={getValuesFromName(startValuesName)}
          endValues={getValuesFromName(endValuesName)}
        />
      )}
    </div>
  );
};

结果.jsx:

export const Result = ({ startValues, endValues }) => {
  console.log("startValues:", startValues.values.length);
  console.log("endValues:", endValues.values.length);

  return (
    <div className="border-4 border-green-400 px-5 py-3">
      <div>:)</div>
      <div>{startValues.name}</div>
      <div>{endValues.name}</div>
    </div>
  );
};

data.js:

export const VALUES = [
  {
    name: "carrot (3)",
    values: [0, 4, 45]
  },
  {
    name: "apple (3)",
    values: [20, 20, 10]
  },
  {
    name: "salad (4)",
    values: [30, 0, 2, 1]
  },
  {
    name: "chicken (6)",
    values: [40, 1, 3, 20, 3, 1]
  }
];

export function getValuesFromName(name) {
  return VALUES.find((d) => d.name === name);
}

问题是,当用户选择值长度不等于的两个项目(例如胡萝卜和鸡肉)时,代码集showResult为真所以Result即使不应该渲染组件也会被渲染。 您可以通过阅读日志消息来检查它。 我试图使用整个示例流程更好地解释自己。

  1. 刷新页面,选中的项目为carrot (3) and apple (3)。这些值具有相同的长度,在控制台中您可以看到:
haveSameNumberOfValues:  true
showResult: true
startValues: 3
endValues: 3

showResult是真的所以Result组件被渲染。好的,可以了

  1. 用户选择chiken (6) as endValuesName。控制台打印:
haveSameNumberOfValues: true
showResult: true
startValues: 3
endValues: 6

haveSameNumberOfValues: false
showResult: false

showResult第一次是正确的,所以Result组件被渲染,然后它发生变化并变为 false。这很奇怪,因为我不想要那样,我想立即拥有showResult=false。这是因为在我的简单示例中,这不会造成大问题,但在我的实际应用程序中它会破坏应用程序。

我的代码有什么问题吗?

我重复一下我想要的:

用户使用 Leva 更改值 ->showResult应在第一次调用之前以正确的方式更新Result


render 在 useEffect 之前调用

这是 React 的本质,你无法改变它!learn https://reactjs.org/docs/hooks-effect.html更多关于使用useEffecthook 和 React 生命周期方法。

我的代码有什么问题吗?

基本上什么都没有。你的代码很好,但是还可以更好


这里有一个简单的解释:

您应该记住的是,当您更新状态时,后者不会立即更新,而是当代码完成执行组件重新渲染时,并且只有当这种情况发生时,状态的值才会更新。
然后当组件完成渲染后,如果有useEffect钩子,它将查看其依赖项数组并检查其中一个或多个值是否与上次渲染期间的值不同,以及情况是否相同useEffect运行(无论如何它都会在第一次渲染期间执行)并且如果代码包含在useEffect钩子正在更新状态,那么这将导致新的渲染,因此组件再次重​​新渲染,这就是您在控制台中得到以下输出的原因:

// component rerendered 
haveSameNumberOfValues: true
showResult: true
startValues: 3
endValues: 6
// useEffet is called (because endValues was 3 and become 6), and since inside useEffect you are updating the state (setShowResult(x) and setHaveSameNumberOfValues(y)) the component rerenders again
// new render
haveSameNumberOfValues: false
showResult: false
// <Result/> is not displayed because showResult is false
// useEffet is not called (because startValuesName and endValuesName are not updated)

现在,话虽这么说,你明白了“问题”,你要做的就是避免触发useEffect更新haveSameNumberOfValues and showResult,没有必要这样做,这也将避免这种额外的渲染。

Since useControlsLeva 与 React “相似”useState每次更新其值时,组件都会重新渲染,这样您就可以在这个新渲染中获得更新的值。然后,当涉及到 JSX 显示 ui 时,您可以调用此函数(它返回TRUE or FALSE)并根据其结果您决定显示<Result /> or <div>Error</div>

该函数包含与您的相同的逻辑useEffect :

function haveValuesTheSameNumberOfItems(first, second) {
  const startValuesItem = getValuesFromName(first);
  const endValuesItem = getValuesFromName(second);
  const startValues = startValuesItem.values;
  const endValues = endValuesItem.values;
  const values = [startValues, endValues];
  const valuesLenght = values.map((path) => path.length);
  return valuesLenght.every((pathLength) => pathLength === valuesLenght[0]);
}

完整的工作示例:

import { useControls } from "leva";
const VALUES = [
  {
    name: "carrot (3)",
    values: [0, 4, 45],
  },
  {
    name: "apple (3)",
    values: [20, 20, 10],
  },
  {
    name: "salad (4)",
    values: [30, 0, 2, 1],
  },
  {
    name: "chicken (6)",
    values: [40, 1, 3, 20, 3, 1],
  },
];
const Result = ({ startValues, endValues }) => {
  console.log("startValues:", startValues.values.length);
  console.log("endValues:", endValues.values.length);

  return (
    <div className="border-4 border-green-400 px-5 py-3">
      <div>{startValues.name}</div>
      <div>{endValues.name}</div>
    </div>
  );
};

const App = () => {
  function getValuesFromName(name) {
    return VALUES.find((d) => d.name === name);
  }

  function haveValuesTheSameNumberOfItems(first, second) {
    const startValuesItem = getValuesFromName(first);
    const endValuesItem = getValuesFromName(second);
    const startValues = startValuesItem.values;
    const endValues = endValuesItem.values;
    const values = [startValues, endValues];
    const valuesLenght = values.map((path) => path.length);
    return valuesLenght.every((pathLength) => pathLength === valuesLenght[0]);
  }

  const { startValuesName, endValuesName } = useControls({
    startValuesName: {
      value: VALUES[0].name,
      options: VALUES.map((d) => d.name),
    },
    endValuesName: {
      value: VALUES[1].name,
      options: VALUES.map((d) => d.name),
    },
  });

  return (
    <div className="w-screen h-screen flex flex-col justify-center items-center">
      {haveValuesTheSameNumberOfItems(startValuesName, endValuesName) ? (
        <Result
          startValues={getValuesFromName(startValuesName)}
          endValues={getValuesFromName(endValuesName)}
        />
      ) : (
        <div>: Error.</div>
      )}
    </div>
  );
};
export default App;

Note:你可以看看useMemo https://react.dev/reference/react/useMemo保留函数结果的记忆版本,包括startValuesName and endValuesName在它的依赖数组中,所以如果有另一个状态可以触发组件重新渲染,React 不必在每次渲染时计算结果,而只在以下情况下计算:startValuesName or endValuesName已更新。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

render 在 useEffect 之前调用 的相关文章

随机推荐

  • 在脚本中调用matlab脚本

    我有两个 matlab 脚本文件 m 不是函数文件 如果我想在当前脚本中调用另一个脚本 我应该使用哪个命令 谢谢 我找到了答案 只需在另一个脚本中命名该脚本即可 myOtherScript 如果您愿意 可以使用 run myOtherScr
  • WCF 客户端传递用户名令牌,并将 MustUnderstand 设置为 true

    我的任务是创建一个将由外部客户端使用的 WCF 服务 客户端使用 WSSE 安全性 具体来说 他们通过 SOAP 标头传递用户名令牌 WCF 服务托管在启用了 SSL 的 IIS 服务器上 至此 我已经有了一个半工作原型 我现在处理的问题是
  • 检测画布内的鼠标单击位置

    我在尝试定义一个单击空白区域的函数时遇到了一个真正的问题 到目前为止 我已经成功地定义了单击一个对象的位置 其中有 10 个 但现在我需要一个单独的函数 以便在不单击任何对象时使用 总体思路可以在以下位置找到 http deciballs
  • IIS 无法将 Windows 凭据传递到 SQL Server for ASP.NET Core 应用程序

    我在一家大公司工作 每个人都有 Intranet 和 Windows AD 登录 我们有许多内部 SQL Server 数据库 允许我们使用 Windows 身份验证登录 我正在尝试通过 ASP NET Core 应用程序连接到其中一个数据
  • 工作表/范围选择(组合)不起作用

    我的 VBA 发生了一些变化 不允许我完成某些例程 我在下面列出了一个非常简单的例子 如果有人经历过此问题 我将非常感谢为解决此问题提供的任何支持 问题的简单示例 当我使用以下代码时 它工作正常 Sheets Sheet1 Select R
  • 使用 T-SQL 计算“nvarchar”字符串的 SHA1 哈希值

    我正在尝试计算SHA1使用 T SQL 计算 unicode 字符串的哈希值 下面的代码可以很好地工作ASCII字符串 declare input varchar 50 set input some text print SHA1 Hash
  • SQL LIKE 条件检查整数?

    我正在使用一组 SQL LIKE 条件来遍历字母表并列出以适当字母开头的所有项目 例如获取标题以字母 A 开头的所有书籍 SELECT FROM books WHERE title ILIKE A 这对于字母来说没问题 但是如何列出以任意数
  • 使用 xpath 和 nightwatch.js 单击动态文本

    我希望在我的应用程序中单击带有动态文本的元素 通常 要单击带有文本的元素 我会执行以下操作 useXpath click contains text some text 对于具有动态名称的测试 我尝试以下操作 useXpath click
  • window.opener 在 Firefox 中为空

    我有一个页面 打开弹出窗口为 openWindow top prcsTypeSelectionPopup event prcsTypeSelection lovWindow width 750 height 550 true dialog
  • 如何在R中保存包含很多点的pdf

    所以我必须保存一个包含很多点的pdf图 那不是问题 问题是当我打开它时 绘制所有这些点需要很长时间 我怎样才能以这样的方式保存这个pdf 当有人打开它时 它不必逐点绘制 如果图片质量下降一点我也没关系 这是一个示例 我认为这不会使您的计算机
  • 在 Android 中清除 AdMob 生成的缓存文件是一个好习惯吗?

    我已将 AdMob 集成到我的 Android 应用程序中 我注意到该应用程序在应用程序数据文件夹内 AdMob 生成的名为 app webview 的文件夹中占用了越来越多的存储空间 我应该在每次应用程序退出时清除此文件夹的内容吗 我应该
  • @Constraint 中清空 validatedBy

    我注意到所有内置约束都有一个空值validatedBy参数输入 Constraint i e Constraint validatedBy 首先 为什么允许它们具有空值validatedBy 我认为您可以仅将不需要额外验证的约束组合留空 另
  • Eclipse 抱怨 Web 应用程序属性

    我的 web xml 中的网络应用程序声明是
  • 如何强制MySQL将0作为有效的自增值

    长话短说 我有一个 SQL 文件 我想将其导入为skelstyle 文件 因此这将以编程方式重复完成 我可以根据需要编辑 SQL 文件 但我不想碰应用程序本身 该应用程序使用userid 0代表匿名用户 它在数据库中还有一个相关 空白 条目
  • 如何使用鼠标改变OpenGL相机

    我正在尝试在 OpenGL 中设置一个相机来查看 3 维中的一些点 为了实现这一点 我不想使用旧的 固定的功能样式 glMatrixMode glTranslate 等 而是自己设置模型视图投影矩阵并在我的顶点着色器中使用它 正交投影就足够
  • 无法从 Activity 调用 getSupportFragmentManager()

    我有一个有片段的活动 XML
  • woocommerce 下订单前检查邮政编码

    由于本地送货是唯一的选择 由于产品送货限制 我不希望客户进入结帐页面并必须填写他们的所有详细信息 然后才发现我们没有送货到他们的邮政编码 因此 我需要在结帐页面上进行与本地配送邮政编码检查相同的功能 但要在结帐过程的早期阶段添加 例如在购物
  • 何时实现接口以及何时扩展超类?

    我读了很多关于 Java 中的接口和类继承的文章 我知道如何做到这两点 并且我认为我对两者都有很好的感觉 但似乎没有人真正将两者并排比较 并解释何时以及为何要使用其中之一 我没有发现很多时候实现接口会比扩展超类更好 那么什么时候实现接口以及
  • 打开屏幕键盘

    我如何从我的应用程序中打开所有 Mac OS X 中内置的键盘查看器 在 10 5 及更早版本中 这是它的路径 因此只需像平常一样启动它即可 System Library Components KeyboardViewer componen
  • render 在 useEffect 之前调用

    我正在尝试使用 React 和 Leva js 创建一个简单的应用程序 基本上有一个项目数组 每个项目都有一个名称和一个数字数组 Leva 面板包含两个选择 用户可以从项目数组中选择两个项目 如果两个选定的项目具有相同的长度 则可以 否则应