React中events的踩坑指南

本文详细介绍了在React项目中如何创建和触发自定义事件来实现跨组件通信,特别强调了如何传递自定义参数以及在事件处理函数中获取最新的state值。通过示例代码展示了在事件监听和触发过程中可能出现的问题及解决方案,包括使用`useCallback`确保回调函数始终引用最新的state值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文介绍react项目中如何创建events来进行跨组件事件触发
重点讲解自定义参数传递,和如何拿到最新的state值🌟

创建和触发 events

// 创建event
const addEvent = (type, listener) => {
  window.addEventListener(type, listener);
};

// 清除event
const removeEvent = (type, listener) => {
  window.removeEventListener(type, listener);
};

// 触发event
const dispatch = (type, value?) => {
  // 触发事件,不传递数据
  // const event = new Event(type);

  // 触发事件,并可以传递参数
  const customEvent = new CustomEvent(type, { detail: value });
  window.dispatchEvent(customEvent);
};

注意⚠️
● 事件type不区分大小写,就是绑定’click’事件,派发’CLICK’或者’Click’,都会引起listener的执行
● new Event(type),触发事件,不能传递额外参数
● new CustomEvent(type),触发事件,可以传递额外value,我们这里把value放在了e.detail属性上,属性名可以自定义

🪤如何传递参数

点击按钮,打印出传递的参数

// 事件名称是不区分大小写
const EventName = 'myClick';

function MyEvent() {
  // 2 点击按钮,触发事件
  const handleClick = () => {
    dispatch(EventName, { name: 'Jack', age: '18' });
  };
   // 1/3 执行回调函数
  const showData = (e) => {
    // 测试传递数据
    const data = e?.detail;
    
    console.log('传递的参数是', data);
  };

  // 1 绑定发布订阅关系
  useEffect(() => {
    addEvent(EventName, showData);

    return () => {
      removeEvent(EventName, showData);
    };
  }, []);

  return (
    <div>
      测试发布订阅模式加参数:
      <button onClick={handleClick}>点击触发事件</button>
    </div>
  );
} 

export default MyEvent;

🪤坑2 如何获取上下文state

逻辑:点击触发事件:打印上下文中的变量v1 和 state值

但是第一次点击按钮结果:
v1 is 变量1
state is
第二次点击按钮结果:
v1 is 变量2
state is
问题:拿不到最新的state值!!!

// 事件名称是不区分大小写
const EventName = 'myClick';

function MyEvent() {
  const [state, setState] = useState('');
  let v1 = '变量1';

  const handleClick = () => {
    dispatch(EventName);
  };
  
  const showData = useCallback(() => {
    // 测试能否获取上下文
    console.log('v1 is ', v1);
    console.log('state is ', state); 

    setState('11111');
    v1 = '变量2';
  }, [state]);
useEffect(() => {
    addEvent(EventName, showData);

    return () => {
      removeEvent(EventName, showData);
    };
  }, []);

  return (
    <div>
      测试发布订阅模式加参数:
      <button onClick={handleClick}>点击触发事件</button>
    </div>
  );
}

export default MyEvent;

但是showData中为什么拿不到最新的state值呢?更准确的说,是回调函数showData中拿不到最新的值。
● 检查一下代码第27行,绑定回调函数是在页面第一次渲染时,
● 之后虽然state变化,但是之前绑定的回调函数不会变化
● 触发事件时,执行的还是页面第一次渲染时绑定的showData,里面的state还是页面第一次渲染时的state
正确代码

// 事件名称是不区分大小写
const EventName = 'myClick';

function MyEvent() {
  const [state, setState] = useState('');
  let v1 = '变量1';

  const showData = useCallback(() => {
    // 测试能否获取上下文
    console.log('v1 is ', v1);
    console.log('state is ', state);

    setState('11111');
    v1 = '变量2';
  }, [state]);

  const handleClick = () => {
    dispatch(EventName);
  };

  useEffect(() => {
    addEvent(EventName, showData);

    return () => {
      removeEvent(EventName, showData);
    };
  }, [showData]); 

  return (
    <div>
      测试发布订阅模式加参数:<br></br>
      <button onClick={handleClick}>点击触发事件</button>
      <br></br>
      <button onClick={() => setState('222')}>点击修改state</button>
      <br></br>
      {state}
    </div>
  );
}

export default MyEvent;

关于以上代码,可能又同学要问:
● 是state变化引起的showData的变化,那么27行useEffect的依赖项写state可以吗?
可以,但不建议。如果showData函数依赖很多state,每次都一个一个写,容易遗漏,也不直观
● useEffect的依赖项是个函数,导致重复渲染怎么办
useEffect对于依赖项是直接对值进行的比较。也就是意味着函数对比的话,就是地址进行比较,显然,每次创建的函数地址都是不同的。不仅仅是state变化,每一次值的改变,都会导致函数改变,所以需要给showData加缓存,如第8行,用useCallback包了一层

React中使用useState是非常常见的,它让我们能够在函数组件中添加状态。然而,有一些常见的陷阱需要注意。下面是一些指南: 1. 错误使用数组解构:当使用数组解构语法来访问useState返回的数组时,确保你使用正确的索引位置。例如,如果你使用了多个useState,确保你在解构时使用正确的索引。否则,你可能会将状态值分配给错误的变量。 2. 对象与合并:当你使用对象作为useState的初始值时,记住useState并不会自动将新的对象与旧的对象合并。相反,它会直接替换整个对象。所以,如果你想保留旧对象的内容并仅更新其中的一部分,你需要手动进行合并。 3. 引用相等性:React使用引用相等性来检测状态的更改。这意味着如果你在更新状态时返回相同的引用,React不会触发重新渲染。因此,确保在更新状态时返回新的引用,而不是直接修改现有对象。 4. 异步更新:当在事件处理程序或异步函数中更新状态时,记得useState是异步的。这意味着多个setState调用可能会被合并在一起,而不是立即更新。如果你需要基于当前状态进行计算,请使用回调函数形式的setState,以便正确地捕获先前的状态。 5. 多个useState:当在一个组件中使用多个useState时,确保给每个useState调用一个有意义的名称。这样可以使代码更清晰,易于理解和维护。 6. 无限循环:在使用useState时,一定要小心避免进入无限循环。例如,如果你在组件的渲染阶段内调用了useState,可能会导致无限循环的情况。解决这个问题的方法是确保条件语句正确地包裹useState的调用。 希望这些指南能够帮助你避免在使用useState时遇到的常见问题!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值