如何避免useRef影响DOM中的其他元素

时间:2020-06-03 21:19:48

标签: javascript html css reactjs react-hooks

我使用此代码作为参考来构建名称https://gist.github.com/estaub/91e54880d77a9d6574b829cb0d3ba021的可滑动列表

该代码成为JSX组件。然后,我有一个包含两个列表的父组件,其想法是在滑动时将其中一个列表中的项目移到第二个列表中。

代码工作正常,当我实现列表时问题就开始了。当我滑动一个Item时,父组件会触发一个函数,该函数获取滑动的Item,从第一个中删除并添加到第二个列表中。父组件再次被渲染。 问题在于,该条目下的被删除的条目似乎正在接收为使条目消失而添加的类。换句话说,就像我要擦除两个项目一样。删除的项目已从列表中删除,但其下面的项目现在仍具有DOM的隐藏属性,因此我们再也看不到它。

出于某种原因,useRef似乎是在选择刚刚删除的元素下方的元素,然后将其应用样式。

有什么想法我可以尝试解决吗?

这是我的可滑动组件

import React, {
  useEffect, useRef,
} from 'react';

  interface IOwnProps {
      onSwipe?: () => void;
  }

const SwipeableListItem:React.FC<IOwnProps> = ({
  children, onSwipe,
}) => {
  const listElementRef = useRef<HTMLDivElement | null>(null);
  const wrapperRef = useRef<HTMLDivElement | null>(null);
  const backgroundRef = useRef<HTMLDivElement | null>(null);

  const dragStartXRef = useRef(0);
  const leftRef = useRef(0);
  const draggedRef = useRef(false);


  useEffect(() => {
    const onSwiped = () => {
      if (onSwipe) {
        onSwipe();
      }
    };

    const onDragEnd = () => {
      if (draggedRef.current) {
        draggedRef.current = false;
        const threshold = 0.3;

        let elementOffsetWidth = 0;
        if (listElementRef.current) {
          elementOffsetWidth = listElementRef.current.offsetWidth;
        }

        if (leftRef.current < elementOffsetWidth * threshold * -1) {
          leftRef.current = (-elementOffsetWidth * 2);

          if (wrapperRef.current) {
            wrapperRef.current.style.maxHeight = (0).toFixed(1);
          }
          onSwiped();
        } else {
          leftRef.current = 0;
        }

        if (listElementRef.current) {
          listElementRef.current.className = 'BouncingListItem';
          listElementRef.current.style.transform = `translateX(${leftRef.current}px)`;
        }
      }
    };


    const onDragEndMouse = (ev: MouseEvent) => {
      window.removeEventListener('mousemove', onMouseMove);
      onDragEnd();
    };

    const onDragEndTouch = (ev: TouchEvent) => {
      window.removeEventListener('touchmove', onTouchMove);
      onDragEnd();
    };


    window.addEventListener('mouseup', onDragEndMouse);
    window.addEventListener('touchend', onDragEndTouch);

    return () => {
      window.removeEventListener('mouseup', onDragEndMouse);
      window.removeEventListener('touchend', onDragEndTouch);
    };
  }, [onSwipe]);


  const onDragStartMouse = (ev: React.MouseEvent) => {
    onDragStart(ev.clientX);
    window.addEventListener('mousemove', onMouseMove);
  };

  const onDragStartTouch = (ev: React.TouchEvent) => {
    const touch = ev.targetTouches[0];
    onDragStart(touch.clientX);
    window.addEventListener('touchmove', onTouchMove);
  };

  const onDragStart = (clientX: number) => {
    draggedRef.current = true;
    dragStartXRef.current = clientX;

    if (listElementRef.current) {
      listElementRef.current.className = 'ListItem';
    }

    requestAnimationFrame(updatePosition);
  };


  const updatePosition = () => {
    if (draggedRef.current) {
      requestAnimationFrame(updatePosition);
    }

    if (listElementRef.current) {
      listElementRef.current.style.transform = `translateX(${leftRef.current}px)`;
    }

    // fade effect
    const opacity = (Math.abs(leftRef.current) / 100);
    if (opacity < 1 && opacity.toString() !== backgroundRef.current?.style.opacity) {
      if (backgroundRef.current) {
        backgroundRef.current.style.opacity = opacity.toString();
      }
    }
  };

  const onMouseMove = (ev: MouseEvent) => {
    const left = ev.clientX - dragStartXRef.current;
    if (left < 0) {
      leftRef.current = left;
    }
  };

  const onTouchMove = (ev: TouchEvent) => {
    const touch = ev.targetTouches[0];
    const left = touch.clientX - dragStartXRef.current;
    if (left < 0) {
      leftRef.current = left;
    }
  };


  return (
    <div className="Wrapper" ref={wrapperRef}>
      <div className="Background" ref={backgroundRef}>
        <span>Remove</span>
      </div>
      <div
        className="ListItem"
        ref={listElementRef}
        onMouseDown={onDragStartMouse}
        onTouchStart={onDragStartTouch}
        role="presentation"
      >
        {children}
      </div>
    </div>
  );
};

export default SwipeableListItem;

这是父组件,我在其中处理列表

import React, {
  useEffect, useState,
} from 'react';
import SwipeableListItem from './SwipeableListItem';


interface ITest{
    id:number;
}

  interface IOwnProps{
      list1?: ITest[];
      list2?: ITest[];
  }

const MyList: React.FC<IOwnProps> = ({
  list1, list2,
}) => {
  const [displayList1, setDisplayList1] = useState<boolean>(true);

  const [list, setList] = useState<Array<Array<ITest>>>([[], []]);

  useEffect(() => {
    setList([list1 || [], list2 || []]);
  }, [list1, list2]);


  const handleSwitchList = () => {
    setDisplayList1(!displayList1);
  };

  //without this function it works, but the item does not get removed from the list
  const handleSendToSecondList = (id: number, myList1: ITest[], myList2: ITest[]) => {
    const foundItem = myList2.find((s) => s.id === id);
    const newList1 = myList1;

    if (foundItem) {
      // push item to list 1
      newList1.push(foundItem);

      // remove list 2
      const newList2 = myList2.filter((s) => s.id !== foundItem.id);

      setList([newList1, newList2]);
    }
  };

  return (
    <div>

      <div>
        <button
          type="button"
          className={`btn  ${displayList1 ? 'btn-primary' : ''}`}
          onClick={handleSwitchList}
        >
          List 1
        </button>
        <button
          type="button"
          className={`btn  ${!displayList1 ? 'btn-primary' : ''}`}
          onClick={handleSwitchList}
        >
          List 2
        </button>
      </div>
      {
          displayList1
            ? list[1]?.map((item) => (
              <SwipeableListItem onSwipe={() => handleSendToSecondList(item.id, list[0], list[1])}>
                <p>{item.id}</p>
              </SwipeableListItem>
            ))
            : list[0]?.map((item) => (
              <SwipeableListItem>
                <p>{item.id}</p>
              </SwipeableListItem>
            ))
        }
    </div>
  );
};

export default MyList;


我创建这个只是为了显示正在发生的事情 https://codesandbox.io/s/react-hooks-usestate-kbvqt?file=/src/index.js

问题在这里发生,但有所不同。有两个列表,如果您从第一个列表中滑动一个项目,则第二个列表中的项目将消失,因为它具有css属性。

0 个答案:

没有答案