Было несколько синтаксических ошибок и отсутствующих зависимостей хуков, которые стали причиной ваших ошибок. Однако вы можете немного упростить свой код с помощью нескольких настроек.
При использовании состояния, которое зависит от другого состояния, я рекомендую объединить его в объект и использовать функцию обратного вызова для его синхронного обновления: setState(prevState => ({ ...prevState, example: "newValue" })
, Это похоже на то, как this.setState();
работает в компоненте на основе классов. Используя один объект и распространяя его свойства ({ ...prevState }
), мы можем перезаписать одно из его свойств, переопределив одно из них ({ ...prevState, newWidth: 0 }
). Этот способ гарантирует, что значения синхронизируются c друг с другом.
Пример ниже следует единому шаблону объекта, упомянутому выше, где newWidth
, newBarAmount
и isDragging
являются свойствами один объект (state
). Затем в примере используется setState
для синхронного обновления / переопределения значений. Кроме того, ссылки были удалены и позволяют перетаскивать панель за окно (если вы этого не хотите, вам нужно ограничить ее в пределах barContainerRef
, как вы делали ранее). В примере также проверяется логическое значение state.isDragging
, когда пользователь щелкает левой кнопкой мыши и удерживает ее на панели. Как только левый щелчок отпущен, перетаскивание отключено.
Вот рабочий пример:
компоненты / бар / индекс. 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;