在react中使用signal

我参考solid的语法 完成了一个能在react里使用signal的npm包

曾经考虑过使用vue或preact的独立reactivity包 但考察过后发现不太符合需求 所以内部实现都是自己完成的

API

提供了与solid很相似的api

  • createSignal 创建signal
ts 复制代码
const [count, setCount] = createSignal(0)
// 正常访问signal
count()
// 不具有响应性的访问
count.value
  • createMemo 创建派生signal.同样可以通过.value方式访问值
  • defineComponent 定义组件.
tsx 复制代码
type CompProps = {
  // 组件参数可以是signal
  num: () => number
}
const Comp = defineComponent<CompProps>((props) => {
  // props对象是signal 返回一个render函数
  // render中用到的signal被自动收集 变化后更新组件
  return () => <span>{props().num()}</span>
})
  • createEffect 创建一个副作用 它会在useEffect阶段被执行 与组件渲染强相关
  • createRef 创建一个react ref 可以直接用在html属性上
  • onCleanup 可以为组件 createMemo createEffect提供清理函数

说明

暂时不适配热重载

使用例

tsx 复制代码
import ReactDOM from 'react-dom/client'
import {
  defineComponent,
  createEffect,
  createMemo,
  createSignal,
  onCleanup,
  createRef,
} from '@fane_the_divine/react-signal'

const Page = defineComponent(() => {
  // 验证基本的响应式能力
  const [count, setCount] = createSignal(0)
  const spanRef = createRef<HTMLSpanElement>()
  const double = createMemo(() => {
    console.log(spanRef.current)
    onCleanup(() => console.log('memo cleanup', count.value))
    return count() * 2
  })
  createEffect(
    (_, isFirst) => {
      console.log(`effect: isFirst:${isFirst} `, spanRef.current)
      onCleanup(() => console.log('cleanup in effect'))
    },
    [double],
  )
  const [show, setShow] = createSignal(true)
  return () => {
    console.log('render')
    return (
      <>
        <button
          onClick={() => {
            setCount(count() + 1)
            setCount(count() + 1)
          }}
        >
          {count()}
        </button>
        <span ref={spanRef}> {double()}</span>
        <button onClick={() => setShow(!show())}>{show() ? 'hidden' : 'show'}</button>
        {show() ? <SubComp1 value={count} /> : null}
      </>
    )
  }
})

const SubComp1 = defineComponent<{ value: () => number }>((props) => {
  // 验证组件销毁后的cleanup被执行
  createEffect(() => {
    onCleanup(() => console.log('SubComp1 unmount in effect'))
  })
  onCleanup(() => console.log('SubComp1 unmount'))
  return () => {
    console.log('%csub comp1 render', 'font-size: 28px; font-weight: bold; color: blue;')
    return <SubComp2 value={props().value} />
  }
})
const SubComp2 = defineComponent<{ value: () => number }>((props) => {
  // 验证signal跨层不重新渲染的能力
  return () => {
    console.log('%csub comp2 render', 'font-size: 28px; font-weight: bold; color: red;')
    return <button>sub comp2 {props().value()}</button>
  }
})
ReactDOM.createRoot(document.getElementById('root')!).render(<Page />)
相关推荐
光影少年42 分钟前
react批量更新、同步/异步更新场景
前端·react.js·掘金·金石计划
YFF菲菲兔2 小时前
completeRoot 源码解析
react.js
光影少年19 小时前
React 合成事件机制、和原生事件区别、事件冒泡阻止
前端·react.js·掘金·金石计划
秃头网友小李1 天前
前端难点:keep-alive 缓存什么?RouterView 的 key 为什么要带 scopeId?
前端·vue.js
YFF菲菲兔1 天前
finishConcurrentRender 源码解析
react.js
YFF菲菲兔1 天前
reconcileChildren 源码解析
react.js
徐小夕1 天前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
奋斗吧程序媛2 天前
补充一个小知识点:有关@click.native
前端·vue.js
英勇无比的消炎药2 天前
一行命令背后:TinyRobot CLI 如何重构 AI 对话接入的效率范式
vue.js·aigc
还有多久拿退休金2 天前
Ant Design Tree 搜索定位避坑指南:虚拟滚动下如何实现高亮与精准定位
前端·react.js