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

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

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

gmono
Copy link

@gmono gmono commented Sep 18, 2024

No description provided.

@gmono gmono changed the title 修复监听失效问题 修复监听失效问题+添加SetupComponent组件支持 Sep 18, 2024
@gmono
Copy link
Author

gmono commented Sep 18, 2024

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组件

@surmon-china
Copy link
Member

  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
Copy link
Member

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> 下无法工作。

@gmono
Copy link
Author

gmono commented Sep 19, 2024

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

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

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

@gmono
Copy link
Author

gmono commented Sep 19, 2024

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

@gmono
Copy link
Author

gmono commented Sep 19, 2024

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

Successfully merging this pull request may close these issues.

2 participants