在反应中删除列表项目保留旧项目

时间:2021-02-25 01:48:29

标签: javascript reactjs

我想从列表中删除所选项目。

当我点击删除正确的项目时,会从列表内容中删除,但在 UI 上,我总是会触发列表项目。

我似乎在跟踪 JSX keys 并显示最后的值。

这是一个demo

const Holidays = (props) => {
  console.log(props);
  const [state, setState] = useState({ ...props });
  useEffect(() => {
    setState(props);
    console.log(state);
  }, []);
  const addNewHoliday = () => {
    const obj = { start: "12/12", end: "12/13" };
    setState(update(state, { daysOffList: { $push: [obj] } }));
  };
  const deleteHoliday = (i) => {
    const objects = state.daysOffList.filter((elm, index) => index != i);

    console.log({ objects });
    setState(update(state, { daysOffList: { $set: objects } }));
    console.log(state.daysOffList);
  };
  return (
    <>
      <Header as="h1" content="Select Holidays" />
      <Button
        primary
        icon={<AddIcon />}
        text
        content="Add new holidays"
        onClick={() => addNewHoliday(state)}
      />

      {state?.daysOffList?.map((elm, i) => {
        console.log(elm.end);
        return (
          <Flex key={i.toString()} gap="gap.small">
            <>
              <Header as="h5" content="Start Date" />
              <Datepicker
                defaultSelectedDate={
                  new Date(`${elm.start}/${new Date().getFullYear()}`)
                }
              />
            </>
            <>
              <Header as="h5" content="End Date" />
              <Datepicker
                defaultSelectedDate={
                  new Date(`${elm.end}/${new Date().getFullYear()}`)
                }
              />
            </>
            <Button
              key={i.toString()}
              primary
              icon={<TrashCanIcon />}
              text
              onClick={() => deleteHoliday(i)}
            />
            <span>{JSON.stringify(state.daysOffList)}</span>
          </Flex>
        );
      })}
    </>
  );
};
export default Holidays;

enter image description here

更新

我正在尝试通过添加时间戳来制作 uniq id。

    return (
      <Flex key={`${JSON.stringify(elm)} ${Date.now()}`} gap="gap.small">
        <>
          <Header as="h5" content="Start Date" />
          <Datepicker
            defaultSelectedDate={
              new Date(`${elm.start}/${new Date().getFullYear()}`)
            }
          />
        </>
        <>
          <Header as="h5" content="End Date" />
          <Datepicker
            defaultSelectedDate={
              new Date(`${elm.end}/${new Date().getFullYear()}`)
            }
          />
        </>
        <Button
          primary
          key={`${JSON.stringify(elm)} ${Date.now()}`}
          icon={<TrashCanIcon />}
          text
          onClick={() => deleteHoliday(i)}
        />{" "}
      </Flex>
    );

我希望错误消失但仍然得到相同的行为

1 个答案:

答案 0 :(得分:1)

问题

您使用数组索引作为 React 键并且您正在改变底层数据数组。当您单击第二个条目将其删除时,第三个元素向前移动以填补空白,现在为刚刚删除的元素分配 React 键。 React 使用密钥来帮助协调,如果密钥保持稳定,React 会重新渲染 UI。

您也不能在队列状态更新后立即控制台日志状态并期望看到更新的状态。

setState(update(state, { daysOffList: { $set: objects } }));
console.log(state.daysOffList);

React 状态更新是异步的,并且在 渲染周期之间进行处理。以上可以并且将只记录当前渲染周期的状态值,而不是为下一个排队的更新 渲染周期。

解决方案

为每个开始/结束数据对象使用 GUID。 uuid 是一个非常棒的包,并且具有非常好的唯一性保证并且使用起来非常简单。

import { v4 as uuidV4 } from 'uuid';

// generate unique id
uuidV4();

要专门解决代码中的问题:

  • 向您的数据添加 id 属性

     const daysOffList = [
       { id: uuidV4(), start: "12/12", end: "12/15" },
       { id: uuidV4(), start: "12/12", end: "12/17" },
       { id: uuidV4(), start: "12/12", end: "12/19" }
     ];
    
     ...
    
     const addNewHoliday = () => {
       const obj = {
         id: uuidV4(),
         start: "12/12",
         end: "12/13",
       };
       setState(update(state, { daysOffList: { $push: [obj] } }));
     };
    
  • 更新处理程序以使用要删除的 id

     const deleteHoliday = (id) => {
       const objects = state.daysOffList.filter((elm) => elm.id !== id);
       setState(update(state, { daysOffList: { $set: objects } }));
     };
    
  • 使用元素 id 属性作为 React 键

     {state.daysOffList?.map((elm, i) => {
       return (
         <Flex key={elm.id} gap="gap.small">
           ...
         </Flex>
       );
     })}
    
  • 将元素 id 传递给删除处理程序

     <Button
       primary
       icon={<TrashCanIcon />}
       text
       onClick={() => deleteHoliday(elm.id)}
     />
    
  • 使用 useEffect React hook 记录任何状态更新

     useEffect(() => {
       console.log(state.daysOffList);
     }, [state.daysOffList]);
    

演示

Edit deleting-list-item-in-react-keep-older-items-instead

注意:如果您不想(或不能)安装额外的 3rd-party 依赖项,那么您可以推出自己的 id 生成器。这将在紧要关头工作,但您真的应该寻求真正经过验证的解决方案。

const genId = ((seed = 0) => () => seed++)();
genId(); // 0
genId(); // 1
相关问题