假設我們有這些要求要實作:
- 按住滑鼠移動時顯示游標位置。
- 下次滑鼠按下時,將先前釋放滑鼠時的最后一個游標位置記錄到控制臺。
這是我的解決方案,它作業正常。但我對它不太滿意:https : //codesandbox.io/s/funny-orla-kdx19?file=/src/App.js
import React, { useState, useEffect } from "react";
import "./styles.css";
const html = document.documentElement;
export default function App() {
const [cursorPos, setCursorPos] = useState({});
useEffect(() => {
const pointerMove = (e) => {
setCursorPos({ x: e.clientX, y: e.clientY });
};
const pointerUp = (e) => {
html.removeEventListener("pointermove", pointerMove);
html.removeEventListener("pointerup", pointerUp);
};
const pointerDown = () => {
console.log("Last position when pointer up", cursorPos);
html.addEventListener("pointermove", pointerMove);
html.addEventListener("pointerup", pointerUp);
};
console.log("registering pointerdown callback");
html.addEventListener("pointerdown", pointerDown);
return () => {
html.removeEventListener("pointerdown", pointerDown);
};
}, [cursorPos]);
return (
<div className="App">
Tracked mouse position: ({cursorPos.x}, {cursorPos.y})
</div>
);
}
如您所見,cursorPos依賴項至關重要。沒有它,pointerDown 偵聽器將不會讀取最新跟蹤的游標位置。然而,它會導致效果回呼被如此頻繁地呼叫,因為 cursorPos 是由 pointermove 事件更新的。并且,pointerdown 偵聽器因此被取消注冊/注冊。
監聽器的這種頻繁重新注冊可能不會造成實際問題。但這在概念上并不是一種簡潔的做事方式。如果可能的話,這是要避免的開銷。這就是為什么我不滿意。
我實際上想出了一些替代方案,但也沒有讓我滿意。
解決方案1:
https://codesandbox.io/s/mystifying-lamport-nr3jk?file=/src/App.js
我們可以在每次滑鼠按下時記錄游標位置,并將其存盤在另一個狀態變數(cursorPosWhenMouseUp)中,該變數將由下一個滑鼠按下事件讀取。由于滑鼠向上事件不是那么頻繁觸發,重新注冊的頻率是可以接受的。這個解決方案的缺點是 cursorPosWhenMouseUp 變數對我來說是多余的,因為最后一個 cursorPos 值應該相同。它的唯一目的是緩解技術問題。讓它留在代碼中并不優雅。
解決方案2:
https://codesandbox.io/s/upbeat-shamir-rhjs4?file=/src/App.js
我們可以將 cursorPos 的另一個副本存盤在 useRef() 的 ref 中(例如cursorPosRef)。由于 ref 在渲染程序中是穩定的,并且不涉及區域變數,我們可以放心從 pointerDown 回呼中安全地讀取它。此解決方案與解決方案 1 存在類似問題,因為我們必須仔細確保 cursorPosRef 始終反映cursorPos值。
解決方案3:
https://codesandbox.io/s/polished-river-psvz7?file=/src/App.js
對于上面提出的冗余問題,我們可以放棄 cursorPos 狀態變數,只使用 cursorPosRef。我們可以直接在 JSX 中讀取它。我們只需要一種從pointermove回呼中強制更新組件的方法。React Hooks FAQ 闡明了這一點。這個解決方案的問題是在 React 方法中不歡迎 forceUpdate 東西。
那么,有沒有更好的方法來實作這些要求呢?
uj5u.com熱心網友回復:
我認為不需要cursorPos作為依賴傳遞,這是我的嘗試:
import React, { useEffect, useState } from 'react';
import './styles.css';
const html = document.documentElement;
export default function App() {
const [cursorPos, setCursorPos] = useState({});
const pointerMove = e => {
setCursorPos({ x: e.clientX, y: e.clientY });
};
useEffect(() => {
html.addEventListener('pointerdown', e => {
html.addEventListener('pointermove', pointerMove);
});
html.addEventListener('pointerup', e => {
html.removeEventListener('pointermove', pointerMove);
});
return () => {
html.removeEventListener('pointerdown');
html.removeEventListener('pointerup');
};
}, []);
return (
<div className="App">
Tracked mouse position: ({cursorPos.x}, {cursorPos.y})
</div>
);
}
https://codesandbox.io/s/eloquent-mendeleev-wqzk4?file=/src/App.js
更新:
為了跟蹤之前的位置,我將使用 useRef:
import React, { useEffect, useRef, useState } from 'react';
const html = document.documentElement;
export default function Test() {
const [cursorCurrentPos, setCursorCurrentPos] = useState({});
const cursorPreviousPos = useRef({});
const pointerMove = e => {
setCursorCurrentPos({ x: e.clientX, y: e.clientY });
};
useEffect(() => {
html.addEventListener('pointerdown', e => {
console.log('Last position when pointer up', cursorPreviousPos.current);
html.addEventListener('pointermove', pointerMove);
});
html.addEventListener('pointerup', e => {
cursorPreviousPos.current = { x: e.clientX, y: e.clientY };
html.removeEventListener('pointermove', pointerMove);
});
return () => {
html.removeEventListener('pointerdown');
html.removeEventListener('pointerup');
};
}, []);
return (
<div className="App">
Tracked mouse position: ({cursorCurrentPos.x}, {cursorCurrentPos.y})
</div>
);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/399858.html
上一篇:過濾反應組件狀態導致無限渲染回圈
