我参考solid的语法 完成了一个能在react里使用signal的npm包
曾经考虑过使用vue或preact的独立reactivity包 但考察过后发现不太符合需求 所以内部实现都是自己完成的
API
提供了与solid很相似的api
- createSignal 创建signal
const [count, setCount] = createSignal(0)
// 正常访问signal
count()
// 不具有响应性的访问
count.value
- createMemo 创建派生signal.同样可以通过
.value方式访问值 - defineComponent 定义组件.
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提供清理函数
说明
暂时不适配热重载
使用例
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 />)