javascript - React 组件 props 在应该改变的时候没有改变?

标签 javascript reactjs react-hooks react-props

我正在从事的一个项目涉及一个大型组件树,向下传递来自顶部组件中一个大型复杂状态对象的 Prop 。

在一个特定的组件“ChecklistEditor”中,我将“subchecklists” Prop 映射到“Subchecklist”组件中,每个组件都有各自的 Prop 。我发现的问题是,当我更新子 list 的状态(在父组件中)时,它会导致 ChecklistEditor 的 Prop (子 list )发生变化(正如我从 devtools 中看到的那样),但即使这些 Prop 被传递给子 list 组件map 函数,Subchecklist 组件的属性不会改变,也不会使用更新后的数据重新渲染。

ChecklistEditor.js

import { useState, useCallback } from "react";

import ChecklistTitle from "../ChecklistTitle/ChecklistTitle";
import Subchecklist from "../Subchecklist/Subchecklist";
import NewSubchecklistForm from "../NewSubchecklistForm/NewSubchecklistForm";
import BlankSpace from "../BlankSpace/BlankSpace";
import Button from "../../UI/Button";
import classes from "./ChecklistEditor.module.css";
import useMemoizedCallback from "../../../hooks/useMemoizedCallback";
import SectionTitle from "../SectionTitle/SectionTitle";

const ChecklistEditor = (props) => {
  const [dragItemIndex, setDragItemIndex] = useState(-1);
  const [draggedOverItemIndex, setDraggedOverItemIndex] = useState(-1);

  // Called when the subchecklist is dragged.
  const handleDrag = useCallback(
    (subchecklistIndex) => {
      setDragItemIndex(subchecklistIndex);
    },
    [setDragItemIndex]
  );

  // Called when another subchecklist is dragged over this subchecklist.
  const handleDragOver = useCallback(
    (itemIndex) => {
      setDraggedOverItemIndex(itemIndex);
    },
    [setDraggedOverItemIndex]
  );

  // Called when the dragend event fires for a subchecklist. (Memoized so that it doesnt cause tons of re-renders).
  const handleDragEnd = useMemoizedCallback(() => {
    props.onReorderSubchecklists(dragItemIndex, draggedOverItemIndex);
  }, [props.onReorderSubchecklists, dragItemIndex, draggedOverItemIndex]);

  // Resets the index of the subchecklist being dragged.
  const resetDragItemIndex = useCallback(() => {
    setDragItemIndex(-1);
  }, [setDragItemIndex]);

  // Map item data into Subchecklist elements
  const subchecklists = props.subchecklists.map((item, index) => {
    let returnItem;
    if (item.type === "subchecklist") {
      returnItem = (
        <Subchecklist
          key={item.id}
          subchecklistIndex={index}
          subchecklistId={item.id}
          title={item.title}
          color={item.color}
          items={item.checkItems}
          onDeleteSubchecklist={props.onDeleteSubchecklist}
          onUpdateSubchecklistTitle={props.onUpdateSubchecklistTitle}
          onChangeColor={props.onChangeSubchecklistColor}
          onItemUpdate={props.onItemUpdate}
          onDeleteItem={props.onDeleteItem}
          onAddCheckItem={props.onAddCheckItem}
          onAddCondition={props.onAddCondition}
          onDrag={handleDrag}
          onDragOver={handleDragOver}
          onDragEnd={handleDragEnd}
          onReorderCheckItems={props.onReorderCheckItems}
          resetDragItemIndex={resetDragItemIndex}
        />
      );
    } else if (item.type === "blankSpace") {
      returnItem = (
        <BlankSpace
          key={item.id}
          id={item.id}
          index={index}
          height={item.height}
          onUpdateHeight={props.onUpdateBlankSpace}
          onDelete={props.onDeleteSubchecklist}
          onDrag={handleDrag}
          onDragOver={handleDragOver}
          onDragEnd={handleDragEnd}
          onReorderCheckItems={props.onReorderCheckItems}
          resetDragItemIndex={resetDragItemIndex}
        />
      );
    } else if (item.type === "sectionTitle") {
      returnItem = (
        <SectionTitle
          key={item.id}
          id={item.id}
          index={index}
          title={item.title}
          color={item.color}
          onUpdateTitle={props.onUpdateSectionTitle}
          onChangeColor={props.onChangeSubchecklistColor}
          onDelete={props.onDeleteSubchecklist}
          onDrag={handleDrag}
          onDragOver={handleDragOver}
          onDragEnd={handleDragEnd}
          onReorderCheckItems={props.onReorderCheckItems}
          resetDragItemIndex={resetDragItemIndex}
        />
      );
    }
    return returnItem;
  });

  return (
    <div className={classes.container}>
      <div className={classes.editor}>
        <ChecklistTitle
          title={props.name}
          onTitleChange={props.onTitleChange}
        />
        <Button type="submit" onClick={props.onSave}>
          Save
        </Button>
        <div className={classes.subchecklistContainer}>{subchecklists}</div>
        <NewSubchecklistForm onSubmit={props.onAddSubchecklist} />
        <Button type="submit" onClick={props.onAddBlankSpace}>
          Add Blank Space
        </Button>
        <Button type="submit" onClick={props.onAddSectionTitle}>
          Add Section Title
        </Button>
      </div>
    </div>
  );
};

export default ChecklistEditor;

我发现的一些奇怪的事情是,如果我对子检查列表状态进行更改,然后对我的代码进行小的更改并保存,nodemon 将重新加载应用程序并且值将更新为它们应该的值。

我真的被困在这上面,不知道发生了什么,所以我会很感激我能得到的任何帮助。谢谢。

编辑: 我应该提一下,如果我从传递给 Subchecklist 组件的任何函数中删除 useCallback Hook ,它会强制组件重新加载并且信息会正确更新,但是这对于重新渲染变得太昂贵了。这似乎也是错误的,因为函数更改是导致重新加载的原因,而数据属性本身应该导致它...

最佳答案

好的,所以我找到了问题。它与复制嵌套状态对象时扩展运算符的工作方式有关。

简而言之;如果您状态中的嵌套对象用作子组件的 Prop ,则仅对整个对象使用展开运算符不足以复制嵌套属性。这是因为展开运算符创建了一个浅拷贝(一层),嵌套对象仍将引用与以前相同的对象,并且您的 props 不会更新以触发重新渲染。

因此,如果我的状态对象如下所示,如果我将内容数组映射到多个子组件并希望它们的更改显示在 props 中并触发组件的重新渲染,我将不得不制作一个副本要修改的内容数组,然后用新值替换旧数组。

const [myObj, setMyObj] = useState({
  name: "MyName",
  id: 1,
  contents: [
    {
      id: 2,
      name: "Nested Name"
    }
  ]
});

错误

let newMyObj = {...myObj};
newMyObj.contents[0].name = "Updated Name";
setMyObj(newMyObj);

let newContents = [...myObj.contents];
newContents[0].name = "Updated Name";
let newMyObj = {...myObj};
newMyObj.contents = newContents;
setMyObj(newMyObj);

关于javascript - React 组件 props 在应该改变的时候没有改变?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71210972/

相关文章:

reactjs - 阻止 FlowType 检查 node_modules 中的错误

javascript - 使用 onChange 更新组件。 React-Hooks

javascript - React Hook "useState"或 React Hook "useEffect"在既不是 React 函数的函数中调用

javascript - 带有只读 token 的 Getstream.io "Not authenticated"

javascript - PrestaShop 1.7 如何获取产品页面上的 id_product_attribute?

javascript - 删除 HERE Maps Javascript API 中的自定义标记不起作用

javascript - 您可以将 Material-UI Link 与 react-router-dom Link 一起使用吗?

javascript - 如何读取react应用程序中当前的URL

javascript - 使用 React hooks 显示来自 jsonplaceholder api 的数据

javascript - Google Recaptcha - GetResponse 不返回 JSON