我在 React 文档中进行了这个练习,并遇到了一个练习,我们需要在 Effect 中添加一个条件。 挑战 2(共 5 个)
我在想我们是否可以在没有 Effect 的情况下做到这一点,并考虑应用引用对象并存储函数,这样它在渲染过程中就不会改变,因此不会依赖于它。 我写了这段代码,但仍然无法工作:
import { useState, useEffect, useRef } from 'react';
export default function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [canMove, setCanMove] = useState(true);
const ref = useRef(null);
// useEffect(() => {
// if(canMove){
// function handleMove(e) {
// setPosition({ x: e.clientX, y: e.clientY });
// }
// window.addEventListener('pointermove', handleMove);
// return () => window.removeEventListener('pointermove', handleMove);
// }
// }, [canMove]);
if(canMove){
ref.current = (e)=> {
setPosition({ x: e.clientX, y: e.clientY });
}
window.addEventListener('pointermove', ref.current);
}
else
window.removeEventListener('pointermove', ref.current);
return (
<>
<label>
<input type="checkbox"
checked={canMove}
onChange={e => setCanMove(e.target.checked)}
/>
The dot is allowed to move
</label>
<hr />
<div style={{
position: 'absolute',
backgroundColor: 'pink',
borderRadius: '50%',
opacity: 0.6,
transform: `translate(${position.x}px, ${position.y}px)`,
pointerEvents: 'none',
left: -20,
top: -20,
width: 40,
height: 40,
}} />
</>
);
}
我在这段代码中遗漏了我想要实现的逻辑。
还有没有什么方法可以在不使用 useEffect 的情况下实现这一目标(只是探索 React)?
当你在没有
useEffect
的情况下编写它时,你会添加很多 pointermove
事件侦听器,因为你更新每个 pointermove
上的位置,所以每次都在线
if(canMove) {
canMove
是true
。这意味着在任何 pointermove
之后,您可以使用 新函数 添加新的事件侦听器,但具有相同的行为
但是当您使用
useEffect
时,仅当canMove
更改时才添加新的事件侦听器,因此如果您不想使用useEffect
,您需要自己检测此变量的更改
您需要手动检测
canMove
的更改并将侦听器保存在useRef
内,因此:
我在组件外部创建了变量
canMovePrev
,它在重新渲染时不会改变。当然,你可以使用另一个useRef
或useState
或其他任何东西来制作同样的东西,我只是更喜欢这样
每次我将函数直接放入
useRef
作为初始值时,都不创建新的侦听器。如果你想使用 null
作为初始值,那么你需要添加额外的检查 ref.current
是否已经是 null
或不是:
if (ref.current !== null) {
ref.current = (e) => {
setPosition({ x: e.clientX, y: e.clientY });
}
}
每次重新渲染后,我都会检查
canMove
的实际值是否与canMovePrev
相同:
canMovePrev
值以进行进一步检查并检查:
canMove
是 true
那么我将添加事件侦听器,但它只会在第一次和删除侦听器后每次添加,因为当您为同一事件传递相同的函数时,它只会添加一次canMove
是 false
那么我删除事件监听器完整代码如下:
const { useState, useEffect, useRef, Fragment } = React;
let canMovePrev = false;
function App() {
const [position, setPosition] = useState({ x: 0, y: 0 });
const [canMove, setCanMove] = useState(true);
const ref = useRef((e) => {
setPosition({ x: e.clientX, y: e.clientY });
});
if(canMove !== canMovePrev) {
canMovePrev = canMove;
if (canMove) window.addEventListener('pointermove', ref.current);
else window.removeEventListener('pointermove', ref.current);
}
return (
<Fragment>
<label>
<input type="checkbox"
checked={canMove}
onChange={e => setCanMove(e.target.checked)}
/>
The dot is allowed to move
</label>
<hr />
<div style={{
position: 'absolute',
backgroundColor: 'pink',
borderRadius: '50%',
opacity: 0.6,
transform: `translate(${position.x}px, ${position.y}px)`,
pointerEvents: 'none',
left: -20,
top: -20,
width: 40,
height: 40,
}} />
</Fragment>
);
}
// Render
const container = document.querySelector('#root');
if (container === null) throw new Error('Root container doesn\'t exist');
ReactDOM.createRoot(container).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>