最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 有关Hook实现getDerivedStateFromProps的小思考

    正文概述 掘金(ZHANGYU)   2021-03-12   675

    用类组件有一个很方便的生命周期就是getDerivedStateFromProps,我用这个生命周期最主要的还是实现一些受控组件。

    但是函数组件没有生命周期的概念,所以自然也没有这个方法了,但是细心的同学一定可以看到官方文档上是有解答过这个问题的。

    在看官方解答之前,先了解一下类组件的getDerivedStateFromProps生命周期是什么时候执行的。

    getDerivedStateFromProps的执行时机

    getDerivedStateFromProps在源码里Mount时和Update时都会触发,并且执行时机是同步的,在源码里就是简单的值的修改,所以也不会发起新的更新。

    const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
    if (typeof getDerivedStateFromProps === 'function') {
      applyDerivedStateFromProps(
        workInProgress,
        ctor,
        getDerivedStateFromProps,
        newProps,
      );
      // 在这里就直接赋值给state了
      instance.state = workInProgress.memoizedState;
    }
    

    官方FAQ的解答

    我想一些同学如果没有看过官方的解答,可能会像下面这样做。

    const [state,setState] = useState()
    useEffect(()=>{
      if('value' in props){
        setState(props.value)
      }
    },[props])
    

    因为我在最初用Hook的时候就这样写过,这样写都不是组件频繁发起更新调度的问题,而是useEffect是异步的,可能会有一些小问题。

    抛出错误的方法,看看官方的解答。

    function ScrollView({row}) {
      const [isScrollingDown, setIsScrollingDown] = useState(false);
      const [prevRow, setPrevRow] = useState(null);
    
      if (row !== prevRow) {
        // Row 自上次渲染以来发生过改变。更新 isScrollingDown。
        setIsScrollingDown(prevRow !== null && row > prevRow);
        setPrevRow(row);
      }
    
      return `Scrolling down: ${isScrollingDown}`;
    }
    

    官方的解答就直接放在函数体中直接修改的值。

    按照逻辑来讲和getDerivedStateFromProps的执行时机是一样的,但是如果你参照这样的方式实现,并在函数体里console一下,就会发现函数体中内部的setState方法好像是触发了函数组件的重新渲染,因为会console多个值。

    原因就在于函数组件无论是要获取新的Hook的值还是干什么的,每次都会重新执行该函数组件,如果是在函数体里执行的setState,React会记录下来。

    简单的看一下源码逻辑。

    // 运行函数组件后返回的children
    let children = Component(props);
    
    // 在函数组件执行过程中发起了更新
    if (didScheduleRenderPhaseUpdateDuringThisPass) {
      children = Component(props);
    }
    

    源码里执行函数组件的过程中如果发起了更新调度,就会同步的再执行一次函数组件来获取新的children值。

    // 执行过程中setState会进入if判断
    if (fiber === currentlyRenderingFiber) {
      // 记录下来是执行过程中发起的更新
      didScheduleRenderPhaseUpdateDuringThisPass = true;
    } else {
      // 发起更新调度
      scheduleUpdateOnFiber(fiber, lane, eventTime);
    }
    

    所以好像触发了重新渲染,实际上只是函数组件再执行了一次,这样有什么问题呢?

    然而官方的解答肯定不会有问题…我想如果是一个超级重的,像以前看别人写的3000行函数组件这样的肯定会有一些小小的性能影响。

    那么问题来了,怎么才能做到像真正的getDerivedStateFromProps生命周期呢?

    我的小想法

    如果使用useStatesetStateprops的值赋值给state的时候必定会让函数组件重新执行,如果我们能手动控制函数组件是否刷新不就完事儿了。

    所以可以使用useRef来存state的值,然后单独执行useState来发起更新调度。

    const useDerivedState = (props) => {
      const rerender = useState()[1];
      const stateRef = useRef();
    
      if (props?.value) {
        stateRef.current = props.value;
      }
    
      const setState = (value) => {
        stateRef.current =
          typeof value === "function" ? value(stateRef.current) : value;
        rerender({});
      };
    
      return [stateRef.current, setState];
    };
    

    这样好像就完美复现了。

    然后我还给封了个包。


    起源地下载网 » 有关Hook实现getDerivedStateFromProps的小思考

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元