如果我将函数存储在引用对象中,它在重新渲染时是否保留其值?

问题描述 投票:0回答:1

我在 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)?

reactjs react-hooks
1个回答
0
投票

问题

当你在没有

useEffect
的情况下编写它时,你会添加很多
pointermove
事件侦听器,因为你更新每个
pointermove
上的位置,所以每次都在线

if(canMove) {

canMove
true
。这意味着在任何
pointermove
之后,您可以使用 新函数 添加新的事件侦听器,但具有相同的行为

但是当您使用

useEffect
时,仅当
canMove
更改时才添加新的事件侦听器,因此如果您不想使用
useEffect
,您需要自己检测此变量的更改

解决方案:

您需要手动检测

canMove
的更改并将侦听器保存在
useRef
内,因此:

  1. 我在组件外部创建了变量

    canMovePrev
    ,它在重新渲染时不会改变。当然,你可以使用另一个
    useRef
    useState
    或其他任何东西来制作同样的东西,我只是更喜欢这样

  2. 每次我将函数直接放入

    useRef
    作为初始值时,都不创建新的侦听器。如果你想使用
    null
    作为初始值,那么你需要添加额外的检查
    ref.current
    是否已经是
    null
    或不是:

    if (ref.current !== null) {
      ref.current = (e) => {
        setPosition({ x: e.clientX, y: e.clientY });
      }
    }
    
  3. 每次重新渲染后,我都会检查

    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>

© www.soinside.com 2019 - 2024. All rights reserved.