我有一个有点复杂的应用程序,其中我正在根据用户输入值制作图表.他们 Select 开始日期和结束日期,以及另一个参数.当这个额外的参数被填充时,它呈现图表.问题是,当用户需要编辑react-datepicker,the start date and end date chosen are updated individually中的日期时,它会更新开始日期,并在用户 Select 结束日期之前将结束日期重置为空,从而导致应用程序出错.我需要弄清楚如何修改状态更新,这样用户就有机会在重新呈现图表之前编辑日期时 Select 一个结束日期.

父零部件:

export const OutagePanel: FC<propTypes> = (props) => {
  const [parameters, setParameters] = useState({
    startDate: null,
    endDate: null,
    unit: 'None'
  })

  const handleDateChange = (range) => {
    const [startDate, endDate] = range;
    setParameters( (prevState) => ({
      ...prevState,
      startDate: startDate,
      endDate: endDate
    }))
  }

  const handleUnitChange = (value) => {
    setParameters( (prevState) => ({
      ...prevState,
      unit: value
    }))
  }

  return (
    <Flex>
      <Flex>
        <Box>Date Range:</Box>
        <Box>
          <DatePicker
            className={styles.datepicker}
            placeholderText="Click to select"
            selected={parameters.startDate}
            startDate={parameters.startDate}
            endDate={parameters.endDate} 
            onChange={handleDateChange}
            showMonthDropdown
            showYearDropdown
            dropdownMode="select"
            minDate={new Date(2000, 0, 1)}
            maxDate={new Date(2021, 0, 5)}
            todayButton="Today"
            selectsRange
          />
        </Box>
      </Flex>
      <GeneratorUnitSelect handleUnitChange={handleUnitChange} />
      {parameters.unit != 'None' && <OutageHistoryChart parameters={parameters}></OutageHistoryChart>}
    </Flex>
  )
}

如上所述,当为parameters.unit != 'None'时,它将显示OutageHistoryChart分量.因此,在它首次成功创建并显示图表后,当用户返回编辑日期时,在日期选取器中第一次单击时,它会将状态更新为如下所示:

parameters = {
  startDate: <user's new date>,
  endDate: null,
  unit: 'Unit 1'
}

由于更新后的状态仍然包含有效值parameters.unit,因此它通过了Return语句中的测试,并try 重新呈现图表.我知道我可以在显示图表之前添加一个额外的测试parameters.endDate != null,这可能会修复它,但是,似乎我应该可以在这里使用useEffect.这就是我try 过的,但在编辑日期范围时跳过了useEffect,并且由于缺少结束日期,它再次无法呈现图表.

export const OutagePanel: FC<propTypes> = (props) => {
  const [parameters, setParameters] = useState({
    startDate: null,
    endDate: null,
    unit: 'None'
  })

  const [showChart, setShowChart] = useState(false)
    
  const handleDateChange = (range) => {
    const [startDate, endDate] = range;
    setParameters( (prevState) => ({
      ...prevState,
      startDate: startDate,
      endDate: endDate
    }))
  }

  useEffect(() => {
    if (parameters.unit != 'None' && parameters.endDate != null) {
      setShowChart(true)
    } else (
      setShowChart(false)
    )
  }, [parameters.startDate, parameters.endDate, parameters.unit])

  //more stuff

然后,我在Return语句中将其更改为:

{showChart && <OutageHistoryChart parameters={parameters}></OutageHistoryChart>}

在这种情况下,useEffect不是一个有效的解决方案,还是我只是实施错误了?只是在我的报税单上做parameter.unit != 'None' && parameter.endDate != null这样的判断看起来很混乱.

推荐答案

使用useEffect挂接来设置showCart状态的问题是,这会打开至少一个渲染周期,其中showCart可能仍为上一次渲染的真,并且parameters.unit不等于"None",而parameters.endDate has已更新为null.为了保护这一点,你仍然需要判断parameter !== "None"和/或parameter.endDate !== null来有条件地呈现OutageHistoryChart.

由于更新showCart状态的延迟,您最好计算showCart值.无论如何,将派生的"状态"存储在react 状态中被认为是一种react 反模式.

示例:

const showCart = parameters.unit !== "None" && !!parameters.endDate;

如果计算派生状态是"昂贵的",那么您应该使用useMemo挂钩来计算104结果值.事实上...只要您发现您已经编写了useState|useEffect组合来保持/更新某些"派生"状态,您可能就应该使用useMemo钩子.

const showCart = useMemo(() => {
  return parameters.unit !== "None" && !!parameters.endDate;
}, [parameters.unit, parameters.endDate]);

完整的代码示例:

export const OutagePanel: FC<propTypes> = (props) => {
  const [parameters, setParameters] = useState({
    startDate: null,
    endDate: null,
    unit: 'None'
  });

  const handleDateChange = (range) => {
    const [startDate, endDate] = range;
    setParameters( (prevState) => ({
      ...prevState,
      startDate,
      endDate
    }));
  };

  const handleUnitChange = (value) => {
    setParameters( (prevState) => ({
      ...prevState,
      unit: value
    }));
  };

  const showCart = parameters.unit !== "None" && !!parameters.endDate;

  return (
    <Flex>
      <Flex>
        <Box>Date Range:</Box>
        <Box>
          <DatePicker
            className={styles.datepicker}
            placeholderText="Click to select"
            selected={parameters.startDate}
            startDate={parameters.startDate}
            endDate={parameters.endDate} 
            onChange={handleDateChange}
            showMonthDropdown
            showYearDropdown
            dropdownMode="select"
            minDate={new Date(2000, 0, 1)}
            maxDate={new Date(2021, 0, 5)}
            todayButton="Today"
            selectsRange
          />
        </Box>
      </Flex>
      <GeneratorUnitSelect handleUnitChange={handleUnitChange} />
      {showCart && <OutageHistoryChart parameters={parameters} />}
    </Flex>
  );
};

鉴于上述情况,我认为更简单的slightly解决方案是,如果endDate104,则将parameters.unit状态"重置"回"None",并将值更新为null或False值.

const handleDateChange = (range) => {
  const [startDate, endDate] = range;
  setParameters( (prevState) => ({
    ...prevState,
    startDate,
    endDate,
    ...!!endDate ? {} : { unit: "None" },
  }));
};

这使当前条件仍能按预期工作.

当然,parameters.unit状态现在可能也应该传递给GeneratorUnitSelect,因此它是一个受控输入,并将"react "并显示当前的单位值(there will need to be a small update to that component to accommodate).

<GeneratorUnitSelect
  handleUnitChange={handleUnitChange}
  value={parameters.unit}
/>
{parameters.unit !== 'None' && <OutageHistoryChart parameters={parameters} />}

Typescript相关问答推荐

使用新控制流的FormArray内部的Angular FormGroup

如何将绑定到实例的类方法复制到类型脚本中的普通对象?

Angular 17 Select 错误core.mjs:6531错误错误:NG 05105:发现意外合成监听器@transformPanel.done

有没有可能使用redux工具包的中间件同时监听状态的变化和操作

使用带有RxJS主题的服务在Angular中的组件之间传递数据

如何根据模板类型实现不同的模板函数行为?

为什么tsx不判断名称中有"—"的属性?'

如何推断计算逻辑的类型

具有泛型类型和缺省值的键

缩小并集子键后重新构造类型

使用订阅时更新AG网格中的行

如何实现异构数组内函数的类型缩小

TypeScrip泛型类未提供预期的类型错误

在VSCode中显示eslint错误,但在终端中运行eslint命令时不显示?(Vite,React,TypeScript)

正确使用相交类型的打字集

错误:NG02200:找不到类型为';对象';的其他支持对象';[对象对象]';.例如数组

作为函数参数的联合类型的键

如何为特定参数定义子路由?

TypeScrip:从嵌套的对象值创建新类型

为什么看起来相似的代码在打字时会产生不同的错误?