【问题标题】:mouseEnter and mouseLeave work on HTML elements, but not React components?mouseEnter 和 mouseLeave 适用于 HTML 元素,但不适用于 React 组件?
【发布时间】:2021-10-08 21:34:05
【问题描述】:

第一次制作 React 网站(使用 Gatsby)。

想要发生什么

在我的索引页面上,当鼠标悬停在 Term 组件上时,我正在尝试使 Note 组件出现。

发生了什么

当我将onMouseEnteronMouseLeave 直接添加到Term 组件时,Note 永远不会出现。但是,如果我使用原生 html 元素(下面的示例代码中的 span)而不是 <Term/>,则会出现 Note

跟箭头功能有关系吗?

  • () => setShowNoteB(true) 替换为console.log("in TermB")(有点)可行。
  • 但使用onMouseEnter={setShowNoteB(true)} 会导致无限循环错误。

这与我如何组成组件有关吗?还是 useState 钩子的限制? 我在这里所做的似乎相当简单/直截了当,但我还是新手,所以也许有一些我不知道的规则。

示例代码

索引页

//index.js

import * as React from 'react';
import { useState } from 'react';
import Term from '../components/term';
import Note from '../components/note';


const IndexPage = () => {
  const [showNoteA, setShowNoteA] = useState(false);
  const [showNoteB, setShowNoteB] = useState(false);

  return (
    <>
      <h1
      >
        <span
          onMouseEnter={() => setShowNoteA(true)}
          onMouseLeave={() => setShowNoteA(false)}
        > TermA* </span>
        {" "} doesn't behave like {" "}
        <Term word="TermB" symbol="†"
          onMouseEnter={() => setShowNoteB(true)}
          onMouseLeave={() => setShowNoteB(false)}
        />
      </h1>

      {showNoteA ? <Note> <p>This is a note for TermA.</p> </Note> : null}
      {showNoteB ? <Note> <p>This is a note for TermB.</p> </Note> : null}
    </>
  );
};

export default IndexPage

术语组件

// term.js

import React from 'react';

const Term = ({ word, symbol }) => {

  return (
    <div>
      <span>{word}</span>
      <span>{symbol}</span>
    </div>
  );
};

export default Term;

笔记组件

// note.js

import * as React from 'react';

const Note = ({ children })=> {
  return (
    <div>
      {children}
    </div>
  )
};

export default Note;

【问题讨论】:

  • 您将这些道具传递给您的术语组件,但您没有使用它们。在运行时, 被一个没有分配悬停事件的
    替换。要使其按预期工作,您的 Term 组件需要如下所示:codesandbox.io/s/angry-glitter-gqf99?file=/src/App.js
  • 啊。这就说得通了。谢谢。如果您将此作为答案发布,我会接受。

标签: javascript reactjs use-state mouseenter


【解决方案1】:

useEffectuseRefforwardRefaddEventListener/removeEventListener 的解决方案:

App.js

import React, { useRef } from 'react';

import Term from './Term';

export default function App() {
  const ref = useRef({});

  return (
    <div>
      Lorem ipsum dolor sit amet,{' '}
      <Term ref={ref} word="consectetur" symbol="†" /> adipiscing elit. Morbi
      laoreet <Term ref={ref} word="lacus" /> in dui vestibulum, nec imperdiet
      augue vulputate.
    </div>
  );
}

如果您想添加其他术语,只需创建一个新的术语组件并添加refwordsymbol(可选)道具。

Term.js

import React, { useState, useEffect, forwardRef } from 'react';

import Note from './Note';

function Term({ word, symbol }, ref) {
  const [isHovered, setHovered] = useState(false);
  const [currentTerm, setCurrentTerm] = useState('');
  const [currentSymbol, setCurrentSymbol] = useState('');

  useEffect(() => {
    if (ref.current && word) {
      ref.current[word].addEventListener('mouseover', handleMouseover);
      ref.current[word].addEventListener('mouseout', handleMouseout);

      return () => {
        ref.current[word].removeEventListener('mouseover', handleMouseover);
        ref.current[word].removeEventListener('mouseout', handleMouseout);
      };
    }
  }, [ref.current, word]);

  const handleMouseover = () => {
    setHovered(true);
    setCurrentTerm(word);
    setCurrentSymbol(symbol);
  };

  const handleMouseout = () => {
    setHovered(false);
    setCurrentTerm('');
    setCurrentSymbol('');
  };

  return (
    <>
      <span ref={(elem) => (ref.current[word] = elem)}>
        {word}
      </span>
      {isHovered ? <Note word={currentTerm} symbol={currentSymbol} /> : null}
    </>
  );
}

const forwarded = forwardRef(Term);
export default forwarded;

Note.js

import React from 'react';

export default function Note({ word, symbol }) {

  const checkTerm = (term) => {
    switch (term) {
      case 'consectetur':
        return `definition`;
      case 'lacus':
        return `definition`;
      default:
        return null;
    }
  };

  return (
    <div>
      <p>
        {word} {symbol ? symbol : '-'}
      </p>
      <p>{checkTerm(word)}</p>
    </div>
  );
}

演示: Stackblitz

【讨论】:

  • 谢谢,我在演示中看到了。但是为什么 useStateonMouseEnter 对于 html 元素的情况就足够了,而在 react 组件的情况下却不行呢?
  • @infieri 用 useRef、forwardRef、EventListener 检查我编辑的答案...否则用 onMouseEnter/onMouseLeave 查看 Chris 的答案
【解决方案2】:

当您在 JSX 中使用组件时,与(小写)HTML 标记相反,任何属性都仅作为 prop 传递,并且没有直接影响。

<Term
  word="TermB" symbol="†"
  onMouseEnter={() => setShowNoteB(true)}
  onMouseLeave={() => setShowNoteB(false)}
/>

您需要获取传递的道具并将其分配给组件的 JSX 中的实际 HTML 元素,如下所示:

const Term = ({ word, symbol, onMouseEnter, onMouseLeave }) => {
  return (
    <div onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
      <span>{word}</span> <span>{symbol}</span>
    </div>
  );
};

【讨论】:

    猜你喜欢
    相关资源
    最近更新 更多
    热门标签