Gathering detailed insights and metrics for react-use-signal
Gathering detailed insights and metrics for react-use-signal
Gathering detailed insights and metrics for react-use-signal
Gathering detailed insights and metrics for react-use-signal
@ivliu/react-signal
Signal(信号)是一种存储应用状态的形式,类似于 React 中的 useState()。但是,有一些关键性差异使 Signal 更具优势。Vue、Preact、Solid 和 Qwik 等流行 JavaScript 框架都支持 Signal。
use-abort-signal
🌠 safely cancel `fetch` requests in `useEffect`
@zpaceway/react-signals
The simplest and easiest-to-use, TypeScript-first, state management solution for React.
use-signal
simple hook to create and dispatch events across a react like app
npm install react-use-signal
Typescript
Module System
Node Version
NPM Version
71.3
Supply Chain
96.5
Quality
75.8
Maintenance
100
Vulnerability
99.6
License
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
8 Stars
39 Commits
1 Watchers
1 Branches
1 Contributors
Updated on May 22, 2025
Latest Version
1.0.14
Package Id
react-use-signal@1.0.14
Unpacked Size
34.80 kB
Size
10.77 kB
File Count
6
NPM Version
6.14.17
Node Version
14.20.1
Published on
Jul 24, 2023
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
4
react-use-signal
非常纯粹,他只解决两个问题,1、数据共享; 2、防止rerender;
它可以让你像使用 useState
一样进行状态管理,同时拥有通过 key
进行共享 state
的能力,并且内部会保证一次渲染周期中,涉及组件最多渲染一次。
当配合 memo
可以最大程度优化你的应用!
🚫声明:目前此工具只适用于React Hooks,class组件请忽略。
如果你的项目只是需要组件间状态共享并且不想引入一些新的概念, 那么use-signal一定适合你:
1、更低的成本: useSignal
可以代替 useState
使用而无需打破你以往的开发习惯
2、更好的性能: useSignal
可以保证一次set操作一个组件最多更新一次甚至是深层组件或列表元素局部更新
3、更小的体积: 打包后只有7kb,gzip以后应该只有3kb
🚫声明:目前是跑最基本的React渲染,并未做任何性能优化,benchmark的结果只能说明use-signal在react之上做了很轻的封装,后面会对局部更新做测试优化
基础使用
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const Child = () => { 4 // 使用名为'app'的Signal 5 const [state] = useSignal('app'); 6 return ( 7 <div>{ state.count }</div> 8 ); 9}; 10 11const App = () => { 12 // 新增并且初始化一个名为'app'的Signal 13 createSignal('app', { count: 0 }); 14 return ( 15 <div> 16 <Child /> 17 </div> 18 ); 19}; 20 21export default App
函数式初始化一个Signal
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const initApp = () => { 4 return { 5 count: 0 6 }; 7}; 8 9const Child = () => { 10 // 使用名为'app'的Signal 11 const [state] = useSignal('app'); 12 return ( 13 <div>{ state.count }</div> 14 ); 15}; 16 17const App = () => { 18 // 新增并且初始化一个名为'app'的Signal 19 createSignal('app', initApp); 20 return ( 21 <div> 22 <Child /> 23 </div> 24 ); 25}; 26 27export default App
初始化一个全局状态(非hooks实现)
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3// 初始化一个名为 'global' 的 Signal; 4// 第二个参数是是否为Hooks实现: 5// true: initSingalManager必须满足hooks原则 6// false: initSingalManager无须满足hooks原则 7// default: true 8initSingalManager({ count: 0 }, false); 9 10const Child = () => { 11 // 使用名为'global'的Signal; 12 // 等同于 useSignal('global'); 13 const [state] = useSignal(); 14 return ( 15 <div>{ state.count }</div> 16 ); 17}; 18 19const App = () => { 20 return ( 21 <div> 22 <Child /> 23 </div> 24 ); 25}; 26 27export default App
🚫注意:initSingalManager 第二个参数不传 或 传 true时,一定要在组件内部声明且符合hooks原则,否则会报错并且打乱React的状态。
初始化一个非hooks实现的Signal
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const [state, setState] = createSignal('app', { count: 0 }, 'key', false); 4 5const Child = () => { 6 // 使用名为'global'的Signal; 7 // 等同于 useSignal('global'); 8 const [state] = useSignal('app'); 9 return ( 10 <div>{ state.count }</div> 11 ); 12}; 13 14const App = () => { 15 return ( 16 <div> 17 <Child /> 18 </div> 19 ); 20}; 21 22export default App
深度订阅模式
有两个场景可使用深度订阅优化性能:
1、当某一个组件依赖列表中的某一项
2、单独更新数组中的某一项
深度订阅特权:
1、set方法不再局限于引用类型,可以是任意类型,相应的值会根据路径改变信号源状态
2、更新范围是可以穿透树形结构进行的,也就是局部刷新
以下为基本深度订阅模式
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const Child = () => { 4 // 深度订阅 signal.count 5 const [count] = useSignal('app', 'count'); 6 return ( 7 <div>count: { count }</div> 8 ); 9}; 10 11const ChildNormal = () => { 12 // 深度订阅 signal.length 13 const [length] = useSignal('app', 'length'); 14 return ( 15 <div>length: { length }</div> 16 ); 17} 18 19const App = () => { 20 const [state, setState] = createSignal('app', { count: 0, length: 0 }); 21 22 // 点击以后发现只有 <Child /> 组件更新了 23 // 实际上 <ChildNormal /> 与 <Child /> 依赖同一个Signal 24 const onAdd = () => { 25 setState({ count: state.count + 1 }); 26 }; 27 28 return ( 29 <div> 30 <div onClick={onAdd}>Click to chang count!</div> 31 <Child /> 32 <ChildNormal /> 33 </div> 34 ); 35}; 36 37export default App
数组深度订阅
Item内部点击事件,只会更新当前点击的Item,并不会刷新整个List
并且setId可以传递任意类型
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const Item = ({index}) => { 4 const [id, setId] = useSignal('app', `list.${index}`); 5 return ( 6 <div onClick={() => setId(123)}>id: { id }</div> 7 ); 8}; 9 10const List = ({list}) => { 11 return list.map((item, i) => <Item key={item} index={i} />); 12}; 13 14const App = () => { 15 createSignal('app', { list: Array.from({length: 10}).map((item, i) => i) }); 16 17 // 点击以后发现只有 <Child /> 组件更新了 18 // 实际上 <ChildNormal /> 与 <Child /> 依赖同一个Signal 19 const onAdd = () => { 20 setState({ count: state.count + 1 }); 21 }; 22 23 return ( 24 <div> 25 <div onClick={onAdd}>Click to chang count!</div> 26 <List list={state.list} /> 27 </div> 28 ); 29}; 30 31export default App
计算属性
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const Child = () => { 4 // 深度订阅 signal.count 5 const [count] = useSignal('app', 'count', (value) => { 6 return `count: ${value}`; 7 }); 8 return ( 9 // count: 0 10 <div>{ count }</div> 11 ); 12}; 13 14const ChildNormal = () => { 15 // 深度订阅 signal.length 16 const [length] = useSignal('app', 'length'); 17 return ( 18 <div>length: { length }</div> 19 ); 20} 21 22const App = () => { 23 const [state, setState] = createSignal('app', { count: 0, length: 0 }); 24 25 // 点击以后发现只有 <Child /> 组件更新了 26 // 实际上 <ChildNormal /> 与 <Child /> 依赖同一个Signal 27 const onAdd = () => { 28 setState({ count: state.count + 1 }); 29 }; 30 31 return ( 32 <div> 33 <div onClick={onAdd}>Click to chang count!</div> 34 <Child /> 35 <ChildNormal /> 36 </div> 37 ); 38}; 39 40export default App
变更合并
以下情况只触发一次render并且最终 count === 3
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const App = () => { 4 createSignal('app', { count: 0 }); 5 const [state, setState] = useSignal('app'); 6 const onAdd = () => { 7 setState({ count: state.count + 1 }); 8 setState({ count: state.count + 2 }); 9 setState({ count: state.count + 3 }); 10 }; 11 12 return ( 13 <div> 14 <div onClick={onAdd}>Click to chang count!</div> 15 <div>count: { state.count }</div> 16 </div> 17 ); 18}; 19 20export default App;
以下情况将会触发两次render
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const App = () => { 4 createSignal('app', { count: 0 }); 5 const [state, setState] = useSignal('app'); 6 const onAdd = () => { 7 setState({ count: state.count + 1 }); 8 setTimeout(() => { 9 // 此时count === 1 10 setState({ count: state.count + 2 }) 11 }); 12 }; 13 14 return ( 15 <div> 16 <div onClick={onAdd}>Click to chang count!</div> 17 <div>count: { state.count }</div> 18 </div> 19 ); 20}; 21 22export default App;
获取变更结果
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const App = () => { 4 createSignal('app', { count: 0 }); 5 const [state, setState] = useSignal('app'); 6 7 const onAdd = () => { 8 setState({ count: state.count + 1 }).then((state) => { 9 console.log(state.count); // 1 10 }); 11 console.log(state.count); // 0 12 }; 13 14 return ( 15 <div> 16 <div onClick={onAdd}>Click to chang count!</div> 17 <div>count: { state.count }</div> 18 </div> 19 ); 20}; 21 22export default App;
高级使用
🚫注意:如果前面的使用已经可以满足你的需求,就不建议你使用以下的能力,因为这可能会给你的程序带来不可控的因素,所以一切尝试都建立在你足够了解use-signal!
Hooks扩展
React的useState有2个参数,这里扩展了四个,多出的两个是use-signal的衍生物
originalSetState: 当前组件真实的set方法,来自于 useState
signal: Signal 的实例,一切数据流转都依赖他
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const Child = () => { 4 const [ state, setState, originalSetState, signal ] = useSignal('app'); 5 return ( 6 <div></div> 7 ); 8}; 9 10const App = () => { 11 createSignal('app', { count: 0 }); 12 return ( 13 <div> 14 <Child /> 15 </div> 16 ) 17}; 18 19export default App;
class SignalManager
Des:SignalManager是中心化管理Signal的类,他非常轻,逻辑也非常简单,内部是由一个Map对象维护的。所以,Signal的name其实也可以是一个对象,但是出于语义化考虑,还是建议使用字符串(例:”app“)来命名。
当然你也可以自定义管理者,请往下看Signal类。
1import { useSignal, createSignal, initSingalManager } from 'react-use-signal'; 2 3const signalManager = initSingalManager({ count: 0 }, false);
class Signal
Des: 这就是发布订阅本身
1import { Signal, SignalManager } from 'react-use-signal';
2
3const mySignal = new Signal({
4 state: {},
5 name: 'app',
6 key: 'app-key',
7 manager: new SignalManager(),
8 autoDestroy: false // 是否是Hooks实现,是顶层 createSigna('app', {}, autoDestroy) 的底层实现
9});
No vulnerabilities found.
No security vulnerabilities found.