在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 />)
相关推荐
Lazy_zheng6 小时前
SDD 实战:用 Claude Code + OpenSpec,把 AI 编程变成“流水线”
前端·react.js·ai编程
光影少年6 小时前
如何实现RN应用的离线功能、数据缓存策略?
react native·react.js·掘金·金石计划
风之舞_yjf7 小时前
Vue基础(31)_插件(plugins)、scoped样式
前端·vue.js
M ? A7 小时前
Vue3+TS实战避坑指南
前端·vue.js·经验分享
Cobyte7 小时前
4.响应式系统基础:从发布订阅模式的角度理解 Vue3 的数据响应式原理
前端·javascript·vue.js
弓.长.8 小时前
ReactNative for OpenHarmony项目鸿蒙化三方库:rn-placeholder — 骨架屏占位组件
react native·react.js·harmonyos
禅思院8 小时前
使用 VueUse 构建一个支持暂停/重置的 CountUp 组件
前端·vue.js·架构
whuhewei8 小时前
React性能优化
前端·react.js·性能优化
下北沢美食家8 小时前
React面试题2
前端·react.js·前端框架
❆VE❆8 小时前
虚拟列表原理与实战运用场景详解
前端·javascript·css·vue.js·html·虚拟列表