在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 />)
相关推荐
Revolution611 小时前
React 组件重新渲染时,到底重新执行了什么
前端·react.js·面试
林焱_RPAAI2 小时前
影刀RPA技术深度:CSS选择器高级实战指南——伪类属性选择器与性能对比完全解析
vue.js·react.js
陳陈陳3 小时前
🚀 前端流式输出革命:SSE + BFF 架构从零到一实战指南
vue.js·架构·node.js
黄泉路醉s8 小时前
在Vant+Vue+TypeScript的H移动前端使用UnoCSS
前端·vue.js·typescript
名字还没想好☜9 小时前
React 受控输入框光标跳到末尾:格式化输入时的 selection 丢失 bug 与修复
前端·javascript·react.js·bug·react·next.js
10share9 小时前
React 新一代样式隔离方案 —— 编译时、零运行时、原生写法
前端·react.js
我要两颗404西柚10 小时前
Stage three:VUE工程化与实战工具
前端·javascript·vue.js
光影少年10 小时前
RN 的EventEmitter 双向通信
前端·react native·react.js
看昭奚恤哭12 小时前
基于 RuoYi-Vue-Pro 定制了一个后台管理系统 magic-admin , 开源出来!
前端·vue.js·开源
GuWenyue1 天前
90%前端写React+TS都踩坑!从组件类型、单向数据流到本地存储完整实战
前端·react.js