react-web applicationに長押しイベントを追加する方法はありますか?
アドレスのリストがあります。任意のアドレスを長押しすると、そのアドレスを削除するイベントを起動し、確認ボックスが続きます。
MouseDown、MouseUp、TouchStart、TouchEndイベントを使用して、長押しイベントとして機能できるタイマーを制御できます。以下のコードをご覧ください
class App extends Component {
constructor() {
super()
this.handleButtonPress = this.handleButtonPress.bind(this)
this.handleButtonRelease = this.handleButtonRelease.bind(this)
}
handleButtonPress () {
this.buttonPressTimer = setTimeout(() => alert('long press activated'), 1500);
}
handleButtonRelease () {
clearTimeout(this.buttonPressTimer);
}
render() {
return (
<div
onTouchStart={this.handleButtonPress}
onTouchEnd={this.handleButtonRelease}
onMouseDown={this.handleButtonPress}
onMouseUp={this.handleButtonRelease}
onMouseLeave={this.handleButtonRelease}>
Button
</div>
);
}
}
React 16.8のフックを使用すると、関数とフックでクラスを書き換えることができます。
import { useState, useEffect } from 'react';
export default function useLongPress(callback = () => {}, ms = 300) {
const [startLongPress, setStartLongPress] = useState(false);
useEffect(() => {
let timerId;
if (startLongPress) {
timerId = setTimeout(callback, ms);
} else {
clearTimeout(timerId);
}
return () => {
clearTimeout(timerId);
};
}, [startLongPress]);
return {
onMouseDown: () => setStartLongPress(true),
onMouseUp: () => setStartLongPress(false),
onMouseLeave: () => setStartLongPress(false),
onTouchStart: () => setStartLongPress(true),
onTouchEnd: () => setStartLongPress(false),
};
}
import useLongPress from './useLongPress';
function MyComponent (props) {
const backspaceLongPress = useLongPress(props.longPressBackspaceCallback, 500);
return (
<Page>
<Button {...backspaceLongPress}>
Click me
</Button>
</Page>
);
};
素敵なフック!しかし、少し改善したいと思います。 useCallback
を使用してイベントハンドラをラップします。これにより、これらがレンダリングごとに変更されないことが保証されます。
import { useState, useEffect, useCallback } from 'react';
export default function useLongPress(callback = () => {}, ms = 300) {
const [startLongPress, setStartLongPress] = useState(false);
useEffect(() => {
let timerId;
if (startLongPress) {
timerId = setTimeout(callback, ms);
} else {
clearTimeout(timerId);
}
return () => {
clearTimeout(timerId);
};
}, [startLongPress]);
const start = useCallback(() => {
setStartLongPress(true);
}, []);
const stop = useCallback(() => {
setStartLongPress(false);
}, []);
return {
onMouseDown: start,
onMouseUp: stop,
onMouseLeave: stop,
onTouchStart: start,
onTouchEnd: stop,
};
}
OnClickおよびonHoldイベントを提供するコンポーネントは次のとおりです-必要に応じて調整してください...
CodeSandbox: https://codesandbox.io/s/hold-press-event-r8q9w
使用法:
import React from 'react'
import Holdable from './holdable'
function App() {
function onClick(evt) {
alert('click ' + evt.currentTarget.id)
}
function onHold(evt) {
alert('hold ' + evt.currentTarget.id)
}
const ids = 'Label1,Label2,Label3'.split(',')
return (
<div className="App">
{ids.map(id => (
<Holdable
onClick={onClick}
onHold={onHold}
id={id}
key={id}
>
{id}
</Holdable>
))}
</div>
)
}
holdable.jsx:
import React from 'react'
const holdTime = 500 // ms
const holdDistance = 3**2 // pixels squared
export default function Holdable({id, onClick, onHold, children}) {
const [timer, setTimer] = React.useState(null)
const [pos, setPos] = React.useState([0,0])
function onPointerDown(evt) {
setPos([evt.clientX, evt.clientY]) // save position for later
const event = { ...evt } // convert synthetic event to real object
const timeoutId = window.setTimeout(timesup.bind(null, event), holdTime)
setTimer(timeoutId)
}
function onPointerUp(evt) {
if (timer) {
window.clearTimeout(timer)
setTimer(null)
onClick(evt)
}
}
function onPointerMove(evt) {
// cancel hold operation if moved too much
if (timer) {
const d = (evt.clientX - pos[0])**2 + (evt.clientY - pos[1])**2
if (d > holdDistance) {
setTimer(null)
window.clearTimeout(timer)
}
}
}
function timesup(evt) {
setTimer(null)
onHold(evt)
}
return (
<div
onPointerDown={onPointerDown}
onPointerUp={onPointerUp}
onPointerMove={onPointerMove}
id={id}
>
{children}
</div>
)
}
注:これはまだSafariでは機能しません-ポインターイベントはv13で提供されます- https://caniuse.com/#feat=pointer