为什么 forEach 循环只在找到状态时设置最后一个值。响应式JS

Why does forEach loop only set the last value if finds to state. ReactJS

const CategoriesData = [
  {
    name: "Category1",
    isActive: true,
    children: [
      {
        name: "Category1Child",
        isActive: false,
      }
    ]
  },
  {
    name: "Category2",
    isActive: false,
  },
  {
    name: "Category3",
    isActive: true,
    children: [
      {
        name: "Category3Child",
        isActive: false,
      }
    ]
  }
];
const [disabledCategories, setDisabledCategories] = useState([]);

function notActiveCategories(categories) {
  // Loop logs out at least 7 isActive: false categories.
  categories.forEach((category) => {
    if (category.isActive) notActiveCategories(category.children);
    if (!category.isActive) {
      setDisabledCategories([...disabledCategories, category]);
      console.log(category);
    }
  });
};

useEffect(() => {
  notActiveCategories(CategoriesData);
  console.log(disabledCategories); // Only 1 category is in the array.
}, []);

我觉得循环调用自身的函数导致 disabledCategories 状态恢复为空时,这导致仅设置 foreach 的最后一步。

那么我如何让它遍历类别数组并使 disabledCategories 状态包含所有具有 isActive: false 的类别对象。 在上面的 CategoriesData 示例中,这意味着 disabledCategories 状态将包含:

[
  {
    name: "Category1Child",
    isActive: false,
  },
  {
    name: "Category2",
    isActive: false,
  },
  {
    name: "Category3Child",
    isActive: false,
  },
];

方式一:递归循环后影响

function notActiveCategoriesRecusive(categories) {
  let notActive = []
  categories.forEach((category) => {
    if (category.isActive) notActive = [...notActive, ...(notActiveCategories(category.children))];
    if (!category.isActive) {
      notActive.push(category)
    }
  });
  return notActive
};

function notActiveCategories(categories) {
   setDisabledCategories(notActiveCategoriesRecusive(categories)
}

方式二:获取最后一个状态因为没有时间刷新

function notActiveCategories(categories) {
  categories.forEach((category) => {
    if (category.isActive) notActiveCategories(category.children);
    if (!category.isActive) {
      setDisabledCategories(oldState => ([...oldState, category]))
    }
  });
};

尝试更改您的 setDisabledCategories 以使用来自 setState:

的先前状态参数
setDisabledCategories(prevState => [...prevState, category])

当多个 setState 调用一起批处理时,您需要小心,以免它们相互覆盖。使用此方法可确保您的 setState 调用是“链接”的,因此您始终可以获得更新的状态。

我只会用过滤后的数组调用 setState 一次:

const findInactive = data =>
  data.filter(e => !e.isActive)
    .concat(...data.filter(e => e.children)
                   .map(e => findInactive(e.children)))
;

const categoriesData = [ { name: "Category1", isActive: true, children: [ { name: "Category1Child", isActive: false, } ] }, { name: "Category2", isActive: false, }, { name: "Category3", isActive: true, children: [ { name: "Category3Child", isActive: false, } ] } ];

const inactive = findInactive(categoriesData)
// the following is neeeded if it's possible for a
// node to have children and be inactive
//  .map(({name, isActive}) => ({name, isActive}))
;
console.log(inactive);
//setDisabledCategories(inactive); // one time in React

这使得代码更容易推理,并将 React 的 API 从过滤逻辑中解耦出来,过滤逻辑可以移出到与 React 无关的通用函数中。

正如其他人提到的,如果您想多次调用setState作为批量更新,您可以使用prevState回调来链接更新:setDisabledCategories(prevState => [...prevState, category]);.