【问题标题】:Access props from a closure function within a functional component从功能组件中的闭包函数访问道具
【发布时间】:2021-09-18 02:43:29
【问题描述】:

我有一个使用 create-react-app 创建的非常简单的 React App。该应用程序显示一个传入 valueonClick 回调的组件,并在触发回调时递增值。

import React, { useState } from 'react';
import Block from './Block';

const App = () => {
  const [count, setCount] = useState(0)

  return (
    <div className="App">
      <Block
        value={count}
        onClick={ () => setCount(count + 1) }
      />
    </div>
  );
}

export default App;

Block 组件采用valueonClick 函数,并在中心显示一个带有值的正方形,并在单击该正方形时调用onClick

const Block = ({ value, onClick }) => {
    return (
        <div
            onClick={onClick}
            style={{
                width: 200,
                height: 200,
                margin: 50,
                background: 'yellow',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                fontSize: 36,
            }}
        >
            {value}
        </div>
    );
}

export default Block;

正如描述的那样,这非常有效。

然后我扩展了这个组件以记录mouseentermouseleave 事件的值,方法是在组件挂载时调用的useEffect 挂钩中添加相关事件处理程序。

import React, { useEffect, useRef } from 'react';


const Block = ({ value, onClick }) => {
    const divRef = useRef();

    const mouseEnter = () => {
        console.log('mouse enter', value);
    }

    const mouseLeave = () => {
        console.log('mouse leave', value);
    }

    useEffect(() => {
        console.log('addEventListeners');
        divRef.current.addEventListener('mouseenter', mouseEnter);
        divRef.current.addEventListener('mouseleave', mouseLeave);
    }, [])

    return (
        <div
            ref={divRef}
            onClick={onClick}
            style={{
                width: 200,
                height: 200,
                margin: 50,
                background: 'yellow',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                fontSize: 36,
            }}
        >
            {value}
        </div>
    );
}

export default Block;

我遇到的问题是这两个事件处理程序是闭包,所以总是打印组件安装时的值。我可以将组件重写为类组件并通过 this 访问道具,但项目中没有其他任何东西使用类组件。我可以将该值复制到一个全局变量中,但这会将我限制在一个实例中。

在闭包中访问一个或多个道具的推荐方法是什么?

我应该补充一点,这是为了演示问题而编写的。在我的应用程序中,值是传入的一个大对象,而 div 是一个画布,上面绘制了数据。

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    最简单的解决方案是使用 React 合成事件并删除 useEffect:

      return <div onMouseEnter={mouseEnter} onMouseLeave={mouseLeave} ... />
    

    【讨论】:

    • 我想过,但没有回答这个问题,因为 OP 说这个例子是为了一个例子的目的。我以为他知道这些
    • 实际上,我不知道使用合成事件会解决我的问题。我确实说过,在完整的应用程序中,div 是一个画布,合成事件与它一起工作。
    【解决方案2】:

    我将创建由useCallback 钩子创建的mouseEntermouseLeave 函数,将value 作为依赖项。然后useEffect 也会将这两个函数作为依赖项。

    然后,您可以使用 useEffect 的返回函数在更改依赖项时执行这一事实,就在调用新的useEffect 之前。现在在返回的函数中只需删除监听器,这样总是只有两个监听器。

    看起来像这样:

       const mouseEnter = useCallback(() => {
            console.log('mouse enter', value);
        }, [value]);
    
        const mouseLeave = useCallback(() => {
            console.log('mouse leave', value);
        }, [value]);
    
        useEffect(() => {
            console.log('addEventListeners');
            divRef.current.addEventListener('mouseenter', mouseEnter);
            divRef.current.addEventListener('mouseleave', mouseLeave);
            return () => {
                divRef.current.removeEventListener('mouseenter', mouseEnter);
                divRef.current.removeEventListener('mouseenter', mouseEnter);
            };
        }, [mouseEnter, mouseLeave])
    

    【讨论】:

      【解决方案3】:

      你需要使用useRef

      const countRef = React.useRef(count);
      

      setCount 替换为

      setCountRef = (value) => {
          countRef.current = value;
          setCount(value);
      }
      

      然后

      const mouseLeave = () => {
          console.log('mouse leave', countRef.current);
      }
      

      【讨论】:

      • 状态变量不要使用useRef,尽量使用useState,加上问题可以通过其他方法解决。
      • @Yooooomi 为什么?会导致什么问题?
      • 我认为这是 useCallbackuseEffect 依赖项的目的,它是在变量更改时更新函数内容,它可以工作但可能不是最干净的 imo,加上事实每当 prop 值发生变化时,您都必须携带 useEffect 来更新 ref 值
      • 谢谢,我对useCallback 不是很熟悉。不,您不必随身携带 useEffect,因为它用既设置 ref 又设置值的新 setValue 替换旧 setValue
      【解决方案4】:

      如果你想将 MouseEnetr 和 MouseLeave 事件添加到 div 元素,你可以很好地实现,如下所示。不需要任何参考。

        return (
            <div
               onClick={() => setValue(value + 1)}
               onMouseEnter={() => mouseEnter(value)}
               onMouseLeave={() => mouseLeave(value)}
                style={{
                    width: 200,
                    height: 200,
                    margin: 50,
                    background: 'yellow',
                    display: 'flex',
                    justifyContent: 'center',
                    alignItems: 'center',
                    fontSize: 36,
                }}
            >
               {value} 
            </div>
      

      如果您正在使用 ref 进行相同的探索,那么据我所知,这可以实现如下所示。

        useEffect(() => {
            console.log('addEventListeners');
            // Remove listeners
            divRef.current.removeEventListener('mouseenter', mouseEnter);
            divRef.current.removeEventListener('mouseleave', mouseLeave);
      
            // Add listeners
            divRef.current.addEventListener('mouseenter', mouseEnter);
            divRef.current.addEventListener('mouseleave', mouseLeave);
        }, [value]) // Pass value as argument, then it behaves as componentDidUpdate
      

      【讨论】:

        猜你喜欢
        • 2020-03-31
        • 1970-01-01
        • 2019-12-29
        • 2021-06-14
        • 2020-10-18
        • 2021-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多