我有一个奇怪的错误,它只在某些时间发生-onChange会触发,但不会更改值。然后,如果我使用onChange函数在输入之外单击,然后在输入框中单击返回,则onChange函数将开始工作。
onChange函数如下:
const handleBarAmountChange = (event) => {
let newWidthAmount = event.target.value / 10;
setNewWidth(newWidthAmount);
setNewBarAmount(event.target.value);
};
父div正在使用带有useRef的引用,该引用已传递给此函数:
import { useEffect, useState } from 'react';
const useMousePosition = (barRef, barInputRef, barContainerRef) => {
const [ mouseIsDown, setMouseIsDown ] = useState(null);
useEffect(() => {
const setMouseDownEvent = (e) => {
if (e.which == 1) {
if (barContainerRef.current.contains(e.target) && !barInputRef.current.contains(e.target)) {
setMouseIsDown(e.clientX);
} else if (!barInputRef.current.contains(e.target)) {
setMouseIsDown(null);
}
}
};
window.addEventListener('mousemove', setMouseDownEvent);
return () => {
window.removeEventListener('mousemove', setMouseDownEvent);
};
}, []);
return { mouseIsDown };
};
onChange是否与eventListener发生冲突?
如何解决这个问题?
答案 0 :(得分:1)
React使用doc中的SyntheticEvent
和Event Pooling
:
事件池
SyntheticEvent
已合并。这意味着在调用事件回调之后,SyntheticEvent
对象将被重用,并且所有属性都将无效。这是出于性能原因。因此,您不能以异步方式访问事件。
您可以在事件上调用event.persist()
或将值存储在新变量中,并按以下方式使用它:
const handleBarAmountChange = (event) => {
// event.persist();
// Or
const { value } = event.target;
let newWidthAmount = value / 10;
setNewWidth(newWidthAmount);
setNewBarAmount(value);
};
答案 1 :(得分:1)
存在一些语法错误和缺少挂钩依赖关系,这些都是导致错误的原因。但是,您可以通过一些调整来简化代码。
当使用依赖于其他状态的状态时,建议将其集中到一个对象中,并使用回调函数来同步更新它:setState(prevState => ({ ...prevState, example: "newValue" })
。这类似于this.setState();
在基于类的组件中的工作方式。通过使用单个对象并扩展其属性({ ...prevState }
),我们可以通过重新定义其中一个属性({ ...prevState, newWidth: 0 }
)覆盖其属性之一。这样可以确保值彼此同步。
下面的示例遵循上述单个对象模式,其中newWidth
,newBarAmount
和isDragging
是单个对象(state
)的属性。然后,该示例使用setState
来同步更新/覆盖这些值。另外,参考文献也已删除,并允许将条形图拖动到窗口上方(如果您不希望这样做,则可以像以前一样将其限制在barContainerRef
内)。当用户左键单击并按住该栏时,该示例还将检查state.isDragging
布尔值。释放左键后,将禁用拖动。
这是一个可行的示例:
components / Bar / index.js
import React, { useEffect, useState, useCallback } from "react";
import PropTypes from "prop-types";
import "./Bar.css";
function Bar({ barName, barAmount, colour, maxWidth }) {
const [state, setState] = useState({
newWidth: barAmount / 2,
newBarAmount: barAmount,
isDragging: false
});
// manual input changes
const handleBarAmountChange = useCallback(
({ target: { value } }) => {
setState(prevState => ({
...prevState,
newWidth: value / 2,
newBarAmount: value
}));
},
[]
);
// mouse move
const handleMouseMove = useCallback(
({ clientX }) => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
newWidth: clientX > 0 ? clientX / 2 : 0,
newBarAmount: clientX > 0 ? clientX : 0
}));
}
},
[state.isDragging]
);
// mouse left click hold
const handleMouseDown = useCallback(
() => setState(prevState => ({ ...prevState, isDragging: true })),
[]
);
// mouse left click release
const handleMouseUp = useCallback(() => {
if (state.isDragging) {
setState(prevState => ({
...prevState,
isDragging: false
}));
}
}, [state.isDragging]);
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
};
}, [handleMouseMove, handleMouseUp]);
return (
<div className="barContainer">
<div className="barName">{barName}</div>
<div
style={{ cursor: state.isDragging ? "grabbing" : "pointer" }}
onMouseDown={handleMouseDown}
className="bar"
>
<svg
width={state.newWidth > maxWidth ? maxWidth : state.newWidth}
height="40"
fill="none"
xmlns="http://www.w3.org/2000/svg"
colour={colour}
>
<rect width={state.newWidth} height="40" fill={colour} />
</svg>
</div>
<div className="barAmountUnit">£</div>
<input
className="barAmount"
type="number"
value={state.newBarAmount}
onChange={handleBarAmountChange}
/>
</div>
);
}
// default props (will be overridden if defined)
Bar.defaultProps = {
barAmount: 300,
maxWidth: 600
};
// check that passed in props match patterns below
Bar.propTypes = {
barName: PropTypes.string,
barAmount: PropTypes.number,
colour: PropTypes.string,
maxWidth: PropTypes.number
};
export default Bar;