【问题标题】:React clone an object from parent props keeps getting updated on re-renderReact 从父道具克隆对象在重新渲染时不断更新
【发布时间】:2021-08-03 21:44:47
【问题描述】:

不确定对此有何修复。我们从 API 中获取对象数组,并使用它在视图中创建数据。同时在一个子组件(过滤器)中,我想映射数据中的一个字段以创建我的过滤器类别。过滤器将使用参数从 API 调用新数据,并返回该数据并更新道具(我认为这是问题,因为这会使用过滤后的数据重新设置子组件以再次设置类别)。

我认为将数组放入 useEffect 会阻止它被更新。

这是我的过滤器组件中的当前 tsx:

const ExclusiveOffersFilters = (props: ExclusiveOffersFiltersProps): JSX.Element => {
  const newFilters = JSON.parse(JSON.stringify(props.options)); // copy the object from props
  let filterOffers;

  useEffect(() => {
    filterOffers = newFilters.map((item: any) => {
      return { value: item.offerType, display: item.offerType };
    });
    console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. 
  }, []);

  return (
    <tr>
      <th>
        <Filter
          options={filterOffers} // here filterOffers is undefined
          alignRight={props.alignRight}
          handleClick={props.handleFilterChange}
          multiSelect={props.multiSelect}
          selectedItems={props.selectedItems}
        />
      </th>
    </tr>
  );
};

我也尝试过 const newFilters = [...props.options]; 和 newFilters = props.options.slice(0) 但这是同一个问题(智慧似乎是使用 JSON 解析/字符串化)。

在我的父组件中,我这样称呼孩子:

&lt;ExclusiveOffersFilters handleFilterChange={props.handleFilterChange} options={props.options} /&gt;

如果我删除 useEffect 函数并仅更新过滤器,它将起作用,但是在选择一个过滤器后,应用程序会更新 api 调用,只接收过滤后的数据,因此过滤器中只设置了一个类别。我需要预防。

我需要一种方法来获取原始类别并阻止它们被更新。

编辑

这是我们的示例数据:

const data = [
  {
    offerId: 1,
    partnerName: "Another offer 4",
    imageUrl:
      "https://www.auctsjpg-w263-h98-99382466814e33da0cfa3d5d2afd921c.jpg",
    description:
      "Save up to 10% on something. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>",
    header: "Save up to 10%",
    enabled: true,
    deleted: false,
    offerType: "Motoring"
  },
  {
    offerId: 0,
    partnerName: "Amazing offer 4",
    imageUrl: "https://www.cGen",
    description:
      "Save up to 20% off* your business insurance thanks to our partnership with<BR/> Churchill Expert. Simply visit the Churchill Expert web page and choose the<BR/> insurance product to suit your business.<BR/>     Churchill Expert will automatically apply the discount to your premium for the<BR/>duration of your policy.     <BR/><BR/>*Minimum premiums apply. Discount applies to NIG policies arranged by Churchill Expert <BR/>and underwritten by U K Insurance Limited.<BR/> Professional Indemnity excluded.",
    header: "Save up to 20% off",
    enabled: true,
    deleted: false,
    offerType: "Insurance"
  },
  {
    offerId: 190,
    partnerName: "Amazing offer 4",
    imageUrl: "https://www.ch.Code=ExpertGen",
    description:
      "Save up to 20% off* your business insurance thanks to our partnership with<BR/> Churchill Expert. Simply visit the Churchill Expert web page and choose the<BR/> insurance product to suit your business.<BR/>     Churchill Expert will automatically apply the discount to your premium for the<BR/>duration of your policy.     <BR/><BR/>*Minimum premiums apply. Discount applies to NIG policies arranged by Churchill Expert <BR/>and underwritten by U K Insurance Limited.<BR/> Professional Indemnity excluded.",
    header: "Save up to 20% off",
    enabled: true,
    deleted: true,
    offerType: "Insurance"
  }
];

export default data;

如果我们根据 offerType = Motoring 进行过滤,那么我们再次调用我们的 api,并将 Motoring 作为我们的过滤器,这只会从上述数据中返回 1 条记录,但现在我们的过滤器会更新并且只显示 1 个 Motoring 过滤器类别。

查看应用中的附加示例。

我已经在此处转储了大部分文件:https://codesandbox.io/s/charming-sinoussi-l2iwh?file=/data.js:0-2061 以及示例数据。我不知道这有多大帮助,当有这么多依赖项时,很难做出一个可行的例子。

父 index.tsx 处理所有 ajax api 调用并将数据传递到 props。 ExclusiveOffersFilters.tsx 收到此信息并进行更新,这是我要停止的部分。正如@Adam 在 cmets 中指出的那样,我们正在尝试解决这个问题,但我认为实际上父母很好,问题在于孩子。

我们确实希望在应用程序的其他地方应用此逻辑,因此性能会很好。我想知道我是否只需要不使用 props.options 并为此使用不同的道具?

【问题讨论】:

  • 听起来您正试图通过在父组件中应用 hack 来解决子组件中的问题(两个错误不代表正确)。发布子组件的代码,我有一种感觉,问题是什么就很明显了。
  • @Adam 我更新了我的问题,并附上大部分文件、示例数据和问题截图的链接。

标签: javascript arrays reactjs react-props


【解决方案1】:
  1. useEffect 执行异步,因此filterOffers 可以在渲染时未定义。

  2. 如果您想基于其他值创建一个值并希望对其进行优化,请使用useMemo 钩子。

  3. 如果您想在组件的重新渲染之间存储一个值,请使用 useRef 钩子。

    const newFilters = useRef(JSON.parse(JSON.stringify(props.options))); // copy the object from props
    const filterOffers = useMemo(() => {
         filterOffers = newFilters.current.map((item: any) => {
             return { value: item.offerType, display: item.offerType };
         });
         console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. 
    }, [newFilters]);
    

【讨论】:

  • 你为什么使用useRef?您是否要将newFilters 作为依赖项添加到useMemo? newFilters 是一个参考,永远不会改变。
  • 也许我误解了这个问题,但我的想法是在重新渲染之间保存它们。
  • 据我了解,OP 希望在重新渲染之间保存原始过滤器,这就是我使用 useRef 的原因
  • 嘿,谢谢你,我似乎无法让它工作。但是您已将 filterOffers 设为 const,然后在 useMemo 函数中再次调用它。我已通过资源链接、示例数据和屏幕截图更新了我的问题。我认为函数应该是const filterOffers = useMemo(() =&gt; { return filterOffers = newFilters.current.map((item: any) =&gt; { return { value: item.offerType, display: item.offerType }; }); console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. }, [newFilters]);
  • @lharby 很抱歉,但是由于这个答案将newFilters 放入useMemo 的依赖数组中,它清楚地表明了这个答案的不连贯性。它对你没有帮助。
【解决方案2】:

这就是你所需要的。如果遇到性能问题,请使用useMemo。如果您没有遇到性能问题,请不要做任何事情

const ExclusiveOffersFilters = (props: ExclusiveOffersFiltersProps): JSX.Element => {

  const filters = props.options.map((item) => ({
    value: item.offerType, 
    display: item.offerType
  }));

  return (
    <tr>
      <th>
        <Filter
          options={filters}
          alignRight={props.alignRight}
          handleClick={props.handleFilterChange}
          multiSelect={props.multiSelect}
          selectedItems={props.selectedItems}
        />
      </th>
    </tr>
  );
};

以useMemo 为例

 const filters = useMemo(() => props.options.map(....),[props.options]);

除非您完全确定自己需要它,否则我无法承受足够的压力来远离 useMemo。

【讨论】:

  • 谢谢,我会试一试,但我怀疑 props.options 会在重新渲染时更新。
  • @lharby 会的,但是我已经重新阅读了您的问题和两件事-首先,整个 JSON.parse JSON.stringify 的东西都是无稽之谈,其次,您正在尝试修复通过向该组件添加 hack 来在您的较低组件中出错。发布您所说的不断更新的较低组件的代码并修复它实际存在的错误,而不是破解这个组件。
  • 好的,我明天去取这个更新,非常感谢您的意见
  • @lharby - 您的 codepen 现在无法正常工作,但我几乎可以保证问题与您在 Filter.tsx 中使用 splice 和 push 有关。当涉及到反应时,不要改变任何东西,状态,道具,任何东西。
猜你喜欢
  • 2018-12-28
  • 1970-01-01
  • 2021-07-13
  • 1970-01-01
  • 2017-01-21
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 2017-10-01
相关资源
最近更新 更多