veact icon indicating copy to clipboard operation
veact copied to clipboard

修复监听失效问题+添加SetupComponent组件支持

Open gmono opened this issue 1 year ago • 7 comments

gmono avatar Sep 18 '24 06:09 gmono


const Temp = defineSetupComponent({
  setup(p: { prop1: string }) {
    const a = ref(1);
    const data = reactive({
      count: 1,
      add() {
        this.count++;
      },
    });
    return {
      a,
      data,
      p: p.prop1,
    };
  },
  render(ctx) {
    return (
      <div>
        <h1>{ctx.p}</h1>
        <h1>{ctx.data.count}</h1>
        <h2>{ctx.a.value}</h2>
        <Button onClick={() => ctx.a.value++}>测试Ref</Button>
        <Button type="primary" onClick={() => ctx.data.add()}>
          测试按钮
        </Button>
      </div>
    );
  },
});

允许编写setup组件

gmono avatar Sep 18 '24 12:09 gmono

  1. useOnce(doWatch) 的实现无法保证幂等,简言之 useRefuseState<React.StrictMode> 下的表现是等价的,并不解决 <React.StrictMode> 下的问题。
  2. onMounted(doWatch) 的实现是有用的,确实解决了问题,但是改变了 useWatch 的执行时机。在 Vue 的设计模式中,因 steup 始终只运行一次,故允许在组件 render 之前产生 watch 带来的同步副作用,特别是使用了 { immediate: true } 或者 watchEffect 时。但这并不符合 React 的设计思想,所以对 <React.StrictMode> 的兼容其实是一个取舍问题。我会更加倾向于后者 React 的理念,但这需要一定程度的重构。
  3. defineSetupComponent 是一个很有创意的想法,社区也已有类似实现,但它并不是 veact 的实现目的,veact 专注在「React 中的 mutable state 管理」。

surmon-china avatar Sep 18 '24 16:09 surmon-china

import React from 'react'
import { useRef, onMounted, useWatch } from 'veact'

export const Component: React.FC = () => {
  const refData = useRef(true)

  const incrementData = () => {
    refData.value++
  }

  onMounted(() => {
    console.log('component mounted')
  })

  useWatch(
    refData,
    () => console.log('refData changed'),
    { immediate: true },
  )

  return (
    <div>
      <pre>ref.value = {JSON.stringify(refData.value, null, 2)}</pre>
      <button onClick={incrementData}>increment ref.value</button>
    </div>
  )
}

在以上组件的示例中,如果采用 onMounted(doWatch) 的设计,则两行日志都必定在组件完全挂载后才会执行,而它们的打印顺序,取决于业务代码的先后顺序,此方案在 <React.StrictMode> 下是有效的。

按照 veact 当前的设计实现,'refData changed' 必定在 'component mounted' 之前打印,且处于组件尚未挂载时的时机,更符合 Vue 的数据组织理念,但与 React「渲染过程不应产生副作用」的理念相悖,且在 <React.StrictMode> 下无法工作。

surmon-china avatar Sep 18 '24 16:09 surmon-china

这个实现已经在strict模式下测试成功,可以正常更新,打印顺序正常,初始在useOnce执行,挂载之前就立刻监听,后使用mount周期处理资源释放 useOnce目前没有发现错误情况

如果要取消挂载时监听 就得取消卸载时取消监听 这个watch就无法释放

或者onbeforeunmount的实现错误,观察到这个hook会在组件第一次执行完成后立刻执行一次导致监听失效

gmono avatar Sep 19 '24 01:09 gmono

实际上vue的组件也会多次挂载卸载 直接声明的watch也会在挂载卸载时停止和激活

gmono avatar Sep 19 '24 01:09 gmono

https://codesandbox.io/p/sandbox/qxpmxd

gmono avatar Sep 19 '24 04:09 gmono