Реагируйте, отменяя функцию clearTimeout, которая не работает должным образом при использовании эффекта

#reactjs #settimeout #cleartimeout #usecallback

Вопрос:

https://codepen.io/evan-jin/pen/qBjGWvR

   const [hoverItem, setHoverItem] = useState(null)
  const timerRef = useRef(null)
  
  const addcursor = useCallback(() => {
    console.log('addcursor')
    clearTimeout(timerRef.current)
    timerRef.current = null
    
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = 'wait'
    }, 10)
  }, [])
  
  const removeCursor = useCallback(() => {
    if (timerRef.current === null) return
    console.log('removeCursor')
    
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = 'default'
    }, 500)
  }, [])
  
  useEffect(() => {
    if (hoverItem) {
      addcursor()
    } else {
      removeCursor()
    }
  }, [hoverItem])
 

Я хочу сохранить «курсор ожидания» при наведении курсора на поле div, но setTimeout срабатывает, когда я перестаю перемещать курсор.

Я пытался использовать clearTimeout при возврате useEffect(willUnmount), но это не работает.

Это работает, если я перемещаюсь внутрь и наружу по каждому компоненту коробки, но если я быстро перемещаю курсор на другой компонент, в конце концов он запускает RemoveCursor setTimeout.

Подводить итоги,

  1. Я хочу сохранить «ожидание курсора» в течение 500 мс, когда я перемещу курсор
  2. После прохождения через коробки «курсор ожидания» должен оставаться на коробке, если курсор находится на ней, а не просто исчезать

этот вопрос выглядит странно, но я только что создал эту простую проблему для своего реального проекта, пожалуйста, кто-нибудь, помогите мне..!

Ответ №1:

Наконец-то я нашел проблему! так как я объявил временную ссылку в каждом компоненте элемента. Таким образом, каждый раз, когда он ссылается на таймер каждого элемента. И я мог бы просто решить эту проблему, объявив timerRef в родительском компоненте! Я оставлю код по этой ссылке. спасибо за ваши усилия, ребята.

Ответ №2:

Похоже, вы используете тайм-аут не обязательно. Вы можете достичь того, что пытаетесь сделать, и без setTimeout этого .

 const { useState, useEffect, useCallback, useRef } = React

const Item = ({ num }) => {
    const [hoverItem, setHoverItem] = useState(null)
  
  const addCaption = useCallback(() => {
    console.log('addCaption')
    document.body.style.cursor = 'wait'
  }, [])
  
  const removeCaption = useCallback(() => {
    console.log('removeCaption')
   
    document.body.style.cursor = 'grab'
  }, [])
  
  useEffect(() => {
    if (hoverItem) {
      addCaption()
    } else {
      removeCaption()
    }
  }, [hoverItem])
  
  return (
    <div 
      className='square'
      name={num}
      id={num}
      onMouseEnter={e => setHoverItem(e.target.id)}
      onMouseLeave={e => setHoverItem(null)}
    />
  )
}

const App = () => {
  
  return (
    <div className='wrapper'>
      {[...Array(12)].map((_, i) => (
        <Item key={i} num={i} />
      ))}
    </div>
  )
}

ReactDOM.render( <App/>, document.getElementById("root") )
 

Комментарии:

1. О, подробностей моего вопроса было недостаточно, и я виноват! на самом деле я хочу сохранить «курсор ожидания» в течение 500 мс, когда я перемещаю курсор

Ответ №3:

Потому что вы представляете состояние для каждого элемента, но вам нужно только одно состояние (обозначающее «курсор находится внутри любого элемента или нет»). Просто переместите свое состояние в родительский компонент, и ваш код будет работать.

 const { useState, useEffect, useCallback, useRef } = React;

const Item = ({ num, onMouseEnter, onMouseLeave }) => {
  return (
    <div
      className="square"
      name={num}
      id={num}
      onMouseEnter={onMouseEnter}
      onMouseLeave={onMouseLeave}
    />
  );
};

const App = () => {
  const [hoverItem, setHoverItem] = useState(null);
  const timerRef = useRef(null);

  const addcursor = useCallback(() => {
    console.log("addcursor");
    document.body.style.cursor = "wait";
    clearTimeout(timerRef.current);
  }, []);

  const removeCursor = useCallback(() => {
    console.log("removecursor");
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      document.body.style.cursor = "default";
    }, 500);
  }, []);

  useEffect(() => {
    if (hoverItem) {
      addcursor();
    } else {
      removeCursor();
    }
  }, [hoverItem]);

  return (
    <div className="wrapper">
      {[...Array(12)].map((_, i) => (
        <Item
          key={i}
          num={i}
          onMouseEnter={(e) => setHoverItem(e.target.id)}
          onMouseLeave={(e) => setHoverItem(null)}
        />
      ))}
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
 

Комментарии:

1. Я пробовал это раньше, и если вы добавите «консоль». войдите в компонент элемента, 12 элементов будут повторно отображаться каждый раз, когда каждый компонент будет зависать. и, наконец, нашли проблему и оставили решение под вашим ответом! кстати, большое спасибо за ваши усилия!