在外部组件的onMouseMove中调用this.setState可防止内部组件的onMouseEnter触发有时。
看起来像onMouseMove中的setState中断或以其他方式阻止内部元素onMouseEnter被触发。我知道setState是异步的但是我不相信我这里有一个陈旧的读取问题,至少在我的调用代码中没有。也许在React中有一个陈旧的读取,或者其他一些bug?我也是React的新手,所以我完全可能忽略了一些显而易见的东西!!
动画GIF演示症状:http://imgur.com/a/9V0WR
为了在重现问题时获得最佳效果,请运行FULL SCREEN代码段,以便查看整个页面。然后将鼠标从列表外部的顶部向下拖动到第0列,并一直向下移动到底部。另请参阅随附的GIF,以证明此问题。
const logEl = document.querySelector('#eventlog')
const logEl2 = document.querySelector('#eventlog2')
const log = (el, msg) => {
el.innerText = msg + '\n' + logEl.innerText
}
const Table = ({ data, onItemHover }) => {
return <table>
<tbody>{data.map((row, y) => (
<tr key={y}>{row.map((item, x) => (
<td key={x} onMouseEnter={() => onItemHover(item)} style={{ color: '#aaa' }}>{item}</td>
))}</tr>
))}</tbody>
</table>
}
const Tooltip = ({ text, position }) => (
<div style={{ position: 'absolute', left: position.x, top: position.y }}>{text}</div>
)
class TableContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
hoveredItem: null,
mouse: { x: 0, y: 0 }
}
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleItemHover = this.handleItemHover.bind(this)
}
handleMouseMove(e) {
const rect = e.currentTarget.getBoundingClientRect()
const mouse = { x: e.pageX - rect.left, y: e.pageY - rect.top }
// XXX: this setState call can prevent the <td onMouseEnter=> from firing
if (this.props.showBug)
this.setState({ mouse })
}
handleMouseLeave(e) {
this.setState({ hoveredItem: null })
}
handleItemHover(hoveredItem) {
this.props.logger('handleItemHover ' + hoveredItem)
this.setState({ hoveredItem })
}
render() {
const {data} = this.props
const {hoveredItem, mouse} = this.state
return <div style={{ position: 'relative' }} onMouseLeave={this.handleMouseLeave} onMouseMove={this.handleMouseMove}>
<Table data={data} onItemHover={this.handleItemHover} />
{hoveredItem && <Tooltip text={hoveredItem} position={mouse} />}
</div>
}
}
const width = 10
const height = 10
const range = (stop) => Array.from(Array(stop).keys())
const data = range(height).map(y => (
range(width).map(x => (
`${x},${y}`
))
))
ReactDOM.render(
<TableContainer data={data} showBug={true} logger={log.bind(null, logEl)} />,
document.getElementById('container')
);
ReactDOM.render(
<TableContainer data={data} showBug={false} logger={log.bind(null, logEl2)} />,
document.getElementById('container2')
);
.col {
width:50%;
float:left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<p>
<strong>
Problem: Calling this.setState in outer component's onMouseMove prevents inner component's onMouseEnter from firing sometimes?
</strong>
<p style="opacity:0.5;">
<strong>Pre-recorded GIFs. Interactive version below.</strong>
<br>
<img src="http://i.imgur.com/xNUxhGI.jpg" style="width:200px; opacity:0.8;">
<img src="http://i.imgur.com/YlNKujg.jpg" style="width:200px; opacity:0.8;">
<br>
<br>
</p>
<small>To reproduce, move mouse around both grids rapidly. Starting outside and moving from top to bottom seems to reproduce it easily.</small>
</p>
<div class="col">
<strong style="color:red">doesn't work</strong>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
<div id="eventlog">
</div>
</div>
<div class="col">
<strong style="color:green">works</strong>
<div id="container2">
<!-- This element's contents will be replaced with your component. -->
</div>
<div id="eventlog2">
</div>
</div>
答案 0 :(得分:0)
您的工具提示正在掩盖表格并直接在鼠标下方,因此mouseEnter
事件不一致。请参阅下面的代码片段,其中我将工具提示偏移50像素,它似乎完美无缺。
const logEl = document.querySelector('#eventlog')
const logEl2 = document.querySelector('#eventlog2')
const log = (el, msg) => {
el.innerText = msg + '\n' + logEl.innerText
}
const Table = ({ data, onItemHover }) => {
return <table>
<tbody>{data.map((row, y) => (
<tr key={y}>{row.map((item, x) => (
<td key={x} onMouseEnter={() => onItemHover(item)} style={{ color: '#aaa' }}>{item}</td>
))}</tr>
))}</tbody>
</table>
}
const Tooltip = ({ text, position }) => (
<div style={{ position: 'absolute', left: position.x, top: position.y }}>{text}</div>
)
class TableContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
hoveredItem: null,
mouse: { x: 0, y: 0 }
}
this.handleMouseLeave = this.handleMouseLeave.bind(this)
this.handleMouseMove = this.handleMouseMove.bind(this)
this.handleItemHover = this.handleItemHover.bind(this)
}
handleMouseMove(e) {
const rect = e.currentTarget.getBoundingClientRect()
const mouse = { x: e.pageX - rect.left + 50, y: e.pageY - rect.top + 50 }
// XXX: this setState call can prevent the <td onMouseEnter=> from firing
if (this.props.showBug)
this.setState({ mouse })
}
handleMouseLeave(e) {
this.setState({ hoveredItem: null })
}
handleItemHover(hoveredItem) {
this.props.logger('handleItemHover ' + hoveredItem)
this.setState({ hoveredItem })
}
render() {
const {data} = this.props
const {hoveredItem, mouse} = this.state
return <div style={{ position: 'relative' }} onMouseLeave={this.handleMouseLeave} onMouseMove={this.handleMouseMove}>
<Table data={data} onItemHover={this.handleItemHover} />
{hoveredItem && <Tooltip text={hoveredItem} position={mouse} />}
</div>
}
}
const width = 10
const height = 10
const range = (stop) => Array.from(Array(stop).keys())
const data = range(height).map(y => (
range(width).map(x => (
`${x},${y}`
))
))
ReactDOM.render(
<TableContainer data={data} showBug={true} logger={log.bind(null, logEl)} />,
document.getElementById('container')
);
ReactDOM.render(
<TableContainer data={data} showBug={false} logger={log.bind(null, logEl2)} />,
document.getElementById('container2')
);
&#13;
.col {
width:50%;
float:left;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<p>
<strong>
Problem: Calling this.setState in outer component's onMouseMove prevents inner component's onMouseEnter from firing sometimes?
</strong>
<p style="opacity:0.5;">
<strong>Pre-recorded GIFs. Interactive version below.</strong>
<br>
<img src="http://i.imgur.com/xNUxhGI.jpg" style="width:200px; opacity:0.8;">
<img src="http://i.imgur.com/YlNKujg.jpg" style="width:200px; opacity:0.8;">
<br>
<br>
</p>
<small>To reproduce, move mouse around both grids rapidly. Starting outside and moving from top to bottom seems to reproduce it easily.</small>
</p>
<div class="col">
<strong style="color:red">doesn't work</strong>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
<div id="eventlog">
</div>
</div>
<div class="col">
<strong style="color:green">works</strong>
<div id="container2">
<!-- This element's contents will be replaced with your component. -->
</div>
<div id="eventlog2">
</div>
</div>
&#13;