【问题标题】:Typescript - failure to update array of objects when using date打字稿 - 使用日期时无法更新对象数组
【发布时间】:2021-03-23 12:27:59
【问题描述】:

我有一个 typescript 函数,它接收一个“queryParameter”,然后从组件内的状态钩子中循环一个现有的 queryParameters 集。如果它找到匹配的参数名称,我希望它将 queryParameters 对象更新为 const 'newQueryParams'。

请注意,该问题仅出现在“日期”查询参数中。所有代码示例都适用于其他类型(“getLatest”和“fundCode”)。这也不是日期对象相等的问题

功能(仅必要部分):

  const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {

    console.log("existing parameters from state hook: ", queryParameters)
    console.log("incoming parameter to function: ", queryParameter)

    const newQueryParams = queryParameters.map((param, i) => {
      console.log("loop: " + i);
      if(queryParameter.name === param.name) {
        console.log("match");
        param.suggestedValue = queryParameter.suggestedValue;
        }
      return param;
  });

  console.log("new query params: ", newQueryParams);

我已经记录了各种位以显示发生了什么:

如您所见,传入参数尝试将日期更改为 21 日,但失败了,尽管循环中有匹配项。

我也尝试过使用深拷贝:

const deepCopyFunction = (inObject: any) => {
  let outObject: any, value, key

  if (typeof inObject !== "object" || inObject === null) {
    return inObject // Return the value if inObject is not an object
  }

  // Create an array or object to hold the values
  outObject = Array.isArray(inObject) ? [] : {}

  for (key in inObject) {
    value = inObject[key]

    // Recursively (deep) copy for nested objects, including arrays
    outObject[key] = deepCopyFunction(value)
  }

  return outObject
}

    const handleQueryParameterChange = (
    queryParameter: DataProductQueryParameter
  ) => {
    
    const queryParametersCopy = deepCopyFunction(queryParameters);
    const deepCopyQueryParam = {...queryParameter};

    console.log("existing parameters copy: ", queryParametersCopy)
    console.log("incoming new parameter: ", queryParameter)

  
    console.log("incoming parameter deep copy: ", deepCopyQueryParam);

    const newQueryParams = queryParametersCopy.map((param, i) => {
      console.log("loop: " + i);
    if(deepCopyQueryParam.name === param.name) {
      console.log("match");
      param.suggestedValue = deepCopyQueryParam.suggestedValue;
    }
     return param;
  });

  console.log("new query params: ", newQueryParams);

产生了相同的结果:

我也尝试过在获得匹配项时只切换整个对象,而不仅仅是编辑属性:

const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {
    
    console.log("existing parameters: ", queryParameters)
    console.log("incoming new parameter: ", queryParameter)

    const newQueryParams = queryParameters.map(param => {
      return queryParameter.name === param.name ? queryParameter : param;    
  });

  console.log("new query params: ", newQueryParams);

...这也不起作用/下面的屏幕截图概述了输出,但还要注意“传入新参数”中标题和扩展对象之间的区别。我已经看过几次了,虽然堆栈溢出告诉我它是控制台的异步性质,但我无法弄清楚它为什么会在代码中发生。

再次请注意,该问题仅出现在“日期”查询参数中。所有代码示例都适用于其他类型(“getLatest”和“fundCode”)

更新

我已经实施了一项建议的改进,但仍然没有得到正确的输出。

下面是更多代码,包括一个状态钩子和一个效果钩子,以及新的实现:


    const [queryParameters, setQueryParameters] = useState<DataProductQueryParameter[]>(dataProductQueryParameters ?? []);

    useEffect(() => {
    console.log("effect called: ", queryParameters)
  }, [queryParameters])

  const handleQueryParameterChange = (queryParameter: DataProductQueryParameter) => {

  if(queryParameter.name === "getLatest") {
    setDatePickerDisabled(!datePickerDisabled);
  } 

  const matchIndex = queryParameters.findIndex(param => queryParameter.name === param.name);
      const queryParametersCopy = JSON.parse(JSON.stringify(queryParameters));  

  if (matchIndex !== -1) {
    const suggestedValue = queryParameter.suggestedValue;
    queryParametersCopy[matchIndex] = { ...queryParametersCopy[matchIndex], suggestedValue}
  }

    console.log("new query params: ", queryParametersCopy);

    setQueryParameters(queryParametersCopy);
  };

状态挂钩在组件加载时成功设置。当我调用 handleChange 函数时,我的调试器显示它成功更新了新数组 (newQueryParams)。

当调用 setQueryParameters 来更新状态挂钩时会出现问题。当调用 useEffect 钩子时,它似乎触发得很好。但是我的调试器和控制台都显示更新数组上的建议值字段没有得到更新。

再一次 - 这对其他字段非常有效 - getLatest 和fundCode,所以逻辑是合理的,并且钩子正在触发以设置新值,但日期根本不会更新。

谢谢

【问题讨论】:

  • 两个具有相同值的Date 对象不相等,除非它们指向相同的引用。换句话说,new Date(2021, 0, 1) !== new Date(2021, 0, 1) 是真的。您需要使用variable.valueOf() === variable.valueOf() 来比较日期。
  • 这能回答你的问题吗? Compare two dates with JavaScript
  • 恐怕不行,我从不测试日期是否相等,只测试对象上的名称字段
  • 我看不出这不起作用的原因,您是否 100% 确定您的 console.log 没有过期?如果你 console.log(newQueryParams.map(p => p.SuggestedValue))
  • 您能否添加一个codesandbox 来说明您的问题?

标签: javascript reactjs typescript object react-hooks


【解决方案1】:

您最初的代码运行良好。如果你运行下面的 sn -p 你会看到最终的new query params 日志显示suggestedValue 属性已经成功更新:

const queryParameters = [{
    name: 'test',
    suggestedValue: '2021-03-23'
  },
  {
    name: 'foobar',
    suggestedValue: '2022-03-23'
  }
]

const handleQueryParameterChange = (queryParameter) => {

  console.log("existing parameters from state hook: ", queryParameters)
  console.log("incoming parameter to function: ", queryParameter)

  const newQueryParams = queryParameters.map((param, i) => {
    console.log("loop: " + i);
    if (queryParameter.name === param.name) {
      console.log("match");
      param.suggestedValue = queryParameter.suggestedValue;
    }
    return param;
  });

  console.log("new query params: ", newQueryParams);

}

handleQueryParameterChange({
  name: 'test',
  suggestedValue: '2021-03-21'
})

但是您可以改进此代码以避免使用.map() 解析数组的所有元素。为此,您最好使用.findIndex() 方法。如果你运行下面的 sn -p 你会看到它只会记录loop: 0:

const queryParameters = [{
    name: 'test',
    suggestedValue: '2021-03-23'
  },
  {
    name: 'foobar',
    suggestedValue: '2022-03-23'
  }
]

const handleQueryParameterChange = (queryParameter) => {

  console.log("existing parameters from state hook: ", queryParameters)
  console.log("incoming parameter to function: ", queryParameter)

  const matchIndex = queryParameters.findIndex((param, i) => {
    console.log("loop: " + i);
    return queryParameter.name === param.name
  })

  if (matchIndex !== -1) {
    const suggestedValue = queryParameter.suggestedValue
    queryParameters[matchIndex] = { ...queryParameters[matchIndex],
      suggestedValue
    }
  }

  console.log("new query params: ", queryParameters);
}

handleQueryParameterChange({
  name: 'test',
  suggestedValue: '2021-03-21'
})

【讨论】:

  • 谢谢,我已编辑问题以获得更多详细信息
【解决方案2】:

我尝试使用具有 2 个状态的小组件来重现您的设置,就像您在更新的问题中显示的那样。此代码(重用您的对象交换)似乎正确更新了queryParameters。您能否尝试使用setQueryParameters(prevQueryParameters =&gt; ...) 重复使用该部分,看看它是否解决了您的问题?

import React, { FC, Fragment, useCallback, useEffect, useState } from 'react';

interface DataProductQueryParameter {
  name: string;
  parameterType: string;
  format: string;
  description: string;
  suggestedValue: string;
}

const dataProductQueryParameters: DataProductQueryParameter[] = [
  {
    name: 'test',
    format: '',
    description: '',
    parameterType: '',
    suggestedValue: '',
  },
  {
    name: 'other',
    format: '',
    description: '',
    parameterType: '',
    suggestedValue: '',
  },
];

export const StackOverflow: FC = () => {
  const [datePickerDisabled, setDatePickerDisabled] = useState(false);
  const [queryParameters, setQueryParameters] = useState<DataProductQueryParameter[]>(
    dataProductQueryParameters,
  );

  useEffect(() => {
    console.log('effect called: ', queryParameters);
  }, [queryParameters]);

  const handleQueryParameterChange = useCallback(
    (queryParameter: DataProductQueryParameter) => {
      if (queryParameter.name === 'getLatest') {
        setDatePickerDisabled(prevDatePickerDisabled => !prevDatePickerDisabled);
      }

      setQueryParameters(prevQueryParameters => 
        prevQueryParameters.map(param => {
          if (param.name === queryParameter.name) {
            return queryParameter;
          }
          return param;
        }),
      );       
    },
    [setQueryParameters, setDatePickerDisabled],
  );

  return (
    <Fragment>
      <button
        onClick={() =>
          handleQueryParameterChange({
            name: 'test',
            description: 'updated',
            format: 'updated',
            parameterType: 'updated',
            suggestedValue: 'updated',
          })
        }
      >
        Update !
      </button>
    </Fragment>
  );
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-08-09
    • 1970-01-01
    • 2020-01-10
    • 2022-01-21
    • 2019-01-13
    • 1970-01-01
    • 2017-10-02
    • 2021-06-20
    相关资源
    最近更新 更多