useEffect 细节

羊羊羊 1年前 ⋅ 1400 阅读
ad

最近在开发支付页时遇到了写问题,记录如下:

首先介绍一下业务需求。支付页面有多种vip套餐类型可供用户选择。用户进入支付页会调一个接口来获取用户是否有优惠券可用,每种优惠券的使用门槛不一样。

这里为了讨论方便,用vip_type指代用户选择的vip类型,coupon_id指代用户选择的优惠券id。

需求一:当用户有多种可用的优惠券时,在用户切换vip时会自动将优惠券更换为面额最大的优惠券。

需求二:支付页面的二维码,生成需要订单号,获取订单号需要vip_type和coupon_id;

首先将vip_type和coupon_id分别用两个state来存储

为了实现需求一,我先使用一个依赖数组中放置了vip_type的useEffect来完成coupon_id的随vip_type切换重新动态选择的逻辑。

useEffect(() => {(
  async () => { 
    // 获取优惠券列表
    const couponist = await getCouponList(); 
    // 计算获取面额最大的优惠券 
    const coupon_id = chooseCoupon(vip_type);
    setCouponId(coupon_id) 
  })()
}, [vip_type])
为了实现需求二,我在生成订单号的useEffect的依赖数组中同时放置了vip_type和coupon_id这两个state,然后操作起来发现,每次切换vip类型时,生成订单号的useEffect都会执行两次,一次是vip_type这个state变化时触发的,当vip_type变化时又会触发coupon_id变化,这又会导致生成订单号的useEffect再执行一次,useEffect第二次才能拿到正确的订单号。
useEffect(() => {
    (async () => {
       // 获取订单号
       const orderId = await getOrderId(vip_type, coupon_id);
       setOrderId(orderId); 
    })()
}, [vip_type, coupon_id])
为了解决useEffect执行两次的问题,我有三种方案
方案一,不使用useEffect来生成订单号的函数,改为在需要的地方手动调。
将生成订单号的接口调用从原先的useEffect中拿出来,单独写成一个函数,并将vip_type和coupon_id当做该函数的参数。然后在切换优惠券的click中调用该函数,在每次切换vip类型后重新计算选择的优惠券的useEffect中也调用一下该函数。
 
方案二,将切换vip_type的click函数改为async,每次切换vip后重新设置coupon_id的逻辑不再放在useEffect中,而是放在vip_type切换的click函数里,这样保证了vip_type和coupon_id同时更新(在同一个批量处理中更新)。同时还要考虑初始化时候的处理。
所以需要将vip_type切换的click函数抽离出来,应用于vip_type切换和初始化时的useEffect。由于vip优惠券有时效性,所以一般在计算使用哪个vip优惠券时,需要调取后端接口。这会导致用户在切换vip_type时,ui的展示会有一定延迟感,交互上欠佳。
 
方案三,问题的核心点在于保证生成订单号的两个变量,是同时更新的。同时由于这两个变量之间又有依赖关系,coupon_id需要依赖更新后的vip_type来做更新。
可以考虑新增一个专门供生成订单号的useEffect依赖的state, 这里我们就叫vip_type_for_effect,它的值和vip_type相同,但更新时机和coupon_id一致。这样就可以保证获取订单号的useEffect只执行一次了,同时保留vip_type,用来保证ui的展示及时性。
 
比较三个方案,方案二有交互上的欠缺,方案三可以看做方案二的升级版。方案一和方案三都差不多,个人在这次需求的实现中选择的是方案一。

关于Webfunny

Webfunny专注于前端监控系统,前端埋点系统的研发。 致力于帮助开发者快速定位问题,帮助企业用数据驱动业务,实现业务数据的快速增长。支持H5/Web/PC前端、微信小程序、支付宝小程序、UniApp和Taro等跨平台框架。实时监控前端网页、前端数据分析、错误统计分析监控和BUG预警,第一时间报警,快速修复BUG!支持私有化部署,Docker容器化部署,可支持千万级PV的日活量!

  点赞 1   收藏 0
  • 羊羊羊
    共发布9篇文章 获得3个收藏
全部评论: 0