Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ahooks源码阅读——lifeCycle #38

Open
jiangshanmeta opened this issue Apr 25, 2021 · 0 comments
Open

ahooks源码阅读——lifeCycle #38

jiangshanmeta opened this issue Apr 25, 2021 · 0 comments
Labels

Comments

@jiangshanmeta
Copy link
Owner

useMount

hook中模拟componentDidMount可以采用useEffect,deps传空数组即可

import { useEffect } from 'react';

const useMount = (fn: () => void) => {
  useEffect(() => {
    fn();
  }, []);
};

export default useMount;

useUnmount

基于hook编写函数组件,模拟类组件的componentWillUnmount钩子,通常是基于useEffect

useEffect(()=>{
    return ()=>{
        console.log('unmount');
    }
},[])

useUnmount这个hook就封装了这一行为

import { useEffect } from 'react';
import usePersistFn from '../usePersistFn';
import { isFunction } from '../utils';

const useUnmount = (fn: any) => {
  const fnPersist = usePersistFn(fn);

  useEffect(
    () => () => {
      if (isFunction(fnPersist)) {
        fnPersist();
      }
    },
    // 为什么需要这个deps呢? 因为eslint会报错的
    // deps又不能直接写[fn], 因为每次render fn都可能会变 这样就不是unmount而是watch功能了
    // 所以需要usePersistFn保证deps传的函数不变
    [fnPersist],
  );
};

export default useUnmount;

useUpdateEffect

在Vue中,watch功能默认是不立即触发,有个immediate参数可以控制是否立即触发。在React中,useEffect默认是立即调用回调,而且没有参数来控制是否立即调用。useUpdateEffect是不立即触发版的useEffect。

实现思路也很简单,依然是基于useEffect,加一个useRef表明是否是第一次。

import { useEffect, useRef } from 'react';
// 注意这里类型标注 使用typeof useEffect 类型签名和useEffect函数一致
const useUpdateEffect: typeof useEffect = (effect, deps) => {
  // 标记是否是第一次
  const isMounted = useRef(false);

  useEffect(() => {
    if (!isMounted.current) {
      isMounted.current = true;
    } else {
      return effect();
    }
  }, deps);
};

export default useUpdateEffect;

useUpdateLayoutEffect

React提供的useLayoutEffect和useEffect非常类似,但是触发的时机是在更新页面之前,同时是同步的。它和useEffect都默认是立即触发回调,useUpdateLayoutEffect提供的是非立即触发版的useLayoutEffect。

实现思路和useUpdateEffect一致,就是用了一个useRef标记位表明是否是第一次。

import { useLayoutEffect, useRef } from 'react';
// 注意类型
const useUpdateLayoutEffect: typeof useLayoutEffect = (effect, deps) => {
  const isMounted = useRef(false);

  useLayoutEffect(() => {
    if (!isMounted.current) {
      isMounted.current = true;
    } else {
      return effect();
    }
  }, deps);
};

export default useUpdateLayoutEffect;

useTrackedEffect

useTrackedEffect是对useEffect的包装,effect函数会被传入以下三个参数:

  • 变化的依赖对应的索引(数组)
  • 旧的依赖
  • 新的依赖
import { useEffect, useRef, DependencyList } from 'react';

const useTrackedEffect = (effect, deps?: DependencyList) => {
  const previousDepsRef = useRef<DependencyList>();
  // 注意返回的是索引数组
  const diffTwoDeps = (deps1, deps2) => {
    // 注意deps是可选的 同时第一次deps1为undefined
    return deps1
      ? deps1.map((_ele, idx) => (deps1[idx] !== deps2[idx] ? idx : -1)).filter((ele) => ele >= 0)
      : deps2
      ? deps2.map((_ele, idx) => idx)
      : [];
  };
  useEffect(() => {
    let changes = diffTwoDeps(previousDepsRef.current, deps);
    const previousDeps = previousDepsRef.current;
    previousDepsRef.current = deps;
    return effect(changes, previousDeps, deps);
  }, deps);
};

export default useTrackedEffect;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant