一个行为的生命周期「react hooks」

一个行为的生命周期

在前端应用中,行为就意味着一次人机交互。在大多数业务场景中,我们都需要对用户操作前、后进行额外的逻辑处理。并且一个操作中会存在状态的流转,操作前->进行时->结束。这些内容都需要我们用代码去书写。

所以,本次使用react来实现一个通用的用户行为记录hooks。

「levle 1」用户行为钩子

在用户交互时,核心逻辑执行前后,我们很多时候需要进行额外的操作。比如执行前校验,执行后埋点上报等等。

useActionsLifeCycle

通过上面的描述,我们知道这个行为存在两个生命周期,before & after


type ActionsLifeCycleCommonFn<T extends any[],R> = (...args:T) => Promise<R>;

const useActionsLifeCycle= <T extends any[], R>(data: {
  before?: ActionsLifeCycleCommonFn<T,R>;
  after?: (...args: T) => void;
}) => {
  const { before, after } = data;

  return useCallback(
    (fn: ActionsLifeCycleCommonFn<T,R>) =>
      async (...args: T) => {
        before && (await before(...args));
        await fn(...args);
        after?.(...args);
      },
    [before, after]
  );
};

测试一下~

一些问题?

可以看到我们很多行为是需要一些时间处理的,然而上面的代码并没有对用户的连点进行额外的处理。

「level 2」行为,在同一时间有且只有一个

「level 1」的问题是用户频繁操作时,会反复执行该动作。大多数业务场景中,是不允许这样操作滴,比如在表单提交中,用户狂点提交按钮,可能会导致创建多个或是其他问题。并且不进行阻止的话,对后端服务器的压力也会增大的哟。毕竟可能会多很多无效请求嘛

阻止用户反复点击,我们通常使用防抖、节流的方式去做。但在这样的场景下并不能完全解决频次的问题,因为一个固定的时间间隔,并不是真实的事件结束。

所以!

useExclusiveAsyncOperation

使用缓存promise的方法,解决时间间隔不准的问题~

type AsyncFunction<T> = (...args: any) => Promise<T>;
const exclusiveAsyncOperation = function <T>(func: AsyncFunction<T>) {
  let activePromise: Promise<T> | null = null;
  return async (...args: any) => {
    if (activePromise) {
      return activePromise;
    }
    activePromise = func(...args);
    try {
      return await activePromise;
    } catch (error) {
      activePromise = null;
      return error;
    } finally {
      activePromise = null;
    }
  };
};

函数版本

type AsyncFunction<T> = (...args: any) => Promise<T>;
const exclusiveAsyncOperation = function <T>(func: AsyncFunction<T>) {
  let activePromise: Promise<T> | null = null;
  return async (...args: any) => {
    if (activePromise) {
      return activePromise;
    }
    activePromise = func(...args);
    try {
      return await activePromise;
    } catch (error) {
      activePromise = null;
      return error;
    } finally {
      activePromise = null;
    }
  };
};

测试一下~

可以看到,用户再疯狂点击,也不会导致反复执行啦~

还可以再进一步吗?

当然啦!现在我们点击按钮之后,再次点击,按钮一点反应都没有!!

用户的操作,需要有回响!

「level 3」 行为状态

用户在「操作A」未完成时,继续执行「下一个操作」,在「level 2」中我们已经完成了阻止。

但是并没有给用户的「下一个」操作回应。所以开发者需要知道「操作A」的执行状态,并把这个状态反馈给用户。

usePromseStatus

我们知道promise中有pending,fulfilled,rejected三个状态,我们可以通过promise的状态,来判断该行为的进行状态。

enum PromiseStatus{
  unStart='unStart',
  pending='pending', 
  fulfilled='fulfilled',
  rejected='rejected',
}

const usePromseStatus =  <T extends any[],R>(promise: (...args:T) => Promise<R>) => {
    const [state, setState] = useState<PromiseStatus>(PromiseStatus.unStart);
    const newPromise = useCallback(async (...args:T) => {
      try{
        setState(PromiseStatus.pending);
        const res = await promise(...args)
        setState(PromiseStatus.fulfilled);
        return res;
      }catch(e){
        setState(PromiseStatus.rejected)
        return e;
      }
    }, [promise])
    return [newPromise, state]
}

测试一下~

总结

通过「level 1-3」的hooks,我们完成了一次对用户行为的通用钩子函数的封装。

使用useActionsLifeCycle监听行为发生前后

使用exclusiveAsyncOperation阻止「操作A」未结束前的多次调用

使用usePromseStatus完成对行为状态的监控,实时的反馈给用户

当然我们这次是三个函数分别使用的,实际上把三个结合在一起封装成一个新的hooks,就好啦!

这里留一个坑~为什么最后不继续封装成一个综合hooks呢?

因为以上的每一个hooks封装都使用了高阶函数,使得函数的层级越来越深。层数越深的代码,对于开发的理解难度就越高。

目前还没有想好怎么把层级拍平,既能保证单个hooks的可用,也能组合成一个层级不那么深的高阶hooks~

原文链接:https://juejin.cn/post/7326923332196122663 作者:小气小憩

(0)
上一篇 2024年1月24日 上午10:16
下一篇 2024年1月24日 上午10:27

相关推荐

发表回复

登录后才能评论