318318 * inserts: Vnode[];
319319 * nodeUpdates: Parameters<typeof updateNode>[];
320320 * removes: (Element | Text)[];
321- * updates: Vnode[] ;
321+ * updates: Set< Vnode> ;
322322 * }} Queues
323323 */
324324
366366
367367/**
368368 * @template T
369- * @typedef {[T, SetState<T>] } State
369+ * @typedef {[T, SetState<T>, SetState<T> ] } State
370370 */
371371
372372const { console, Element, queueMicrotask } = globalThis ;
@@ -430,6 +430,30 @@ export const Try = props => {
430430 return props . children ;
431431} ;
432432
433+ /**
434+ * @param {Props } prev
435+ * @param {Props } next
436+ */
437+ const defaultMemo = ( prev , next ) => {
438+ if ( prev === next ) return true ;
439+
440+ for ( const key in prev ) if ( prev [ key ] !== next [ key ] ) return false ;
441+
442+ for ( const key in next ) if ( ! ( key in prev ) ) return false ;
443+
444+ return true ;
445+ } ;
446+
447+ /**
448+ * @template {Component} T
449+ * @param {T } Component
450+ * @param {typeof defaultMemo } [memo]
451+ */
452+ export const memo = ( Component , memo = defaultMemo ) => {
453+ Component . memo = memo ;
454+ return Component ;
455+ } ;
456+
433457/**
434458 * @template T
435459 * @param {T } value
@@ -492,64 +516,6 @@ const createNode = (type, props, parentNode) => {
492516 ) ;
493517} ;
494518
495- /**
496- * @template {Element} Node
497- * @param {Node } node
498- * @param {keyof Node } key
499- */
500- const isSimpleProperty = ( node , key ) =>
501- key in node && ( node [ key ] == null || ! isObject ( node [ key ] ) ) ;
502-
503- /**
504- * @template {Element | Text} T
505- * @param {T } node
506- * @param {Props<T> } prev
507- * @param {Props<T> } next
508- */
509- const updateNode = ( node , prev , next ) => {
510- if ( prev === next ) return ;
511-
512- // Element.prototype.TEXT_NODE
513- if ( node . nodeType === 3 ) {
514- node . nodeValue = /** @type {string } */ ( next . nodeValue ) ;
515- return ;
516- }
517-
518- for ( const key in prev ) {
519- if ( key === 'children' || key in next ) continue ;
520-
521- if ( key === 'ref' ) {
522- if ( prev . ref ) prev . ref . current = null ;
523- // @ts -expect-error assignment of a readonly property would fail
524- } else if ( isSimpleProperty ( node , key ) ) node [ key ] = '' ;
525- else /** @type {Element } */ ( node ) . removeAttribute ( key ) ;
526- }
527-
528- for ( const key in next ) {
529- if ( key === 'children' || prev [ key ] === next [ key ] ) continue ;
530-
531- if ( key === 'ref' ) {
532- if ( prev . ref ) prev . ref . current = null ;
533- if ( next . ref ) next . ref . current = node ;
534- } else if ( key === 'style' && isObject ( next [ key ] ) ) {
535- const { style } = /** @type {HTMLElement | SVGElement } */ ( node ) ;
536- const prevStyle = /** @type {Partial<CSSStyleDeclaration> } */ (
537- isObject ( prev [ key ] ) ? prev [ key ] : emptyObject
538- ) ;
539- const nextStyle = /** @type {Partial<CSSStyleDeclaration> } */ ( next [ key ] ) ;
540- for ( const key in prevStyle ) if ( ! ( key in nextStyle ) ) style [ key ] = '' ;
541- for ( const key in nextStyle ) style [ key ] = nextStyle [ key ] ?? '' ;
542- // @ts -expect-error assignment of a readonly property would fail
543- } else if ( isSimpleProperty ( node , key ) ) node [ key ] = next [ key ] ?? '' ;
544- else if ( next [ key ] != null ) {
545- /** @type {Element } */ ( node ) . setAttribute (
546- key ,
547- /** @type {string } */ ( next [ key ] )
548- ) ;
549- } else /** @type {Element } */ ( node ) . removeAttribute ( key ) ;
550- }
551- } ;
552-
553519/** @type {Vnode | null } */
554520let currentVnode = null ;
555521
@@ -572,6 +538,21 @@ export const useRef = initial => {
572538 return ref ;
573539} ;
574540
541+ /**
542+ * @param {unknown[] } before
543+ * @param {unknown[] } after
544+ */
545+ const depsChanged = ( before , after ) => {
546+ if ( before === after ) return false ;
547+
548+ let { length } = before ;
549+ if ( length !== after . length ) return true ;
550+
551+ while ( length -- ) if ( before [ length ] !== after [ length ] ) return true ;
552+
553+ return false ;
554+ } ;
555+
575556/**
576557 * @param {AfterEffect } fn
577558 * @param {unknown[] } [deps]
@@ -601,6 +582,48 @@ export const useMemo = (fn, deps = emptyArray) => {
601582 return ref . current . value ;
602583} ;
603584
585+ /**
586+ * @param {Vnode } a
587+ * @param {Vnode } b
588+ */
589+ const batchComparator = ( a , b ) => {
590+ while ( a . depth > b . depth ) {
591+ a = /** @type {Vnode } */ ( a . parent ) ;
592+ if ( a === b ) return 1 ;
593+ }
594+
595+ while ( b . depth > a . depth ) {
596+ b = /** @type {Vnode } */ ( b . parent ) ;
597+ if ( b === a ) return - 1 ;
598+ }
599+
600+ while ( a . parent !== b . parent ) {
601+ a = /** @type {Vnode } */ ( a . parent ) ;
602+ b = /** @type {Vnode } */ ( b . parent ) ;
603+ }
604+
605+ return a . index - b . index ;
606+ } ;
607+
608+ /** @param {Vnode } vnode */
609+ const queueUpdate = vnode => {
610+ if ( vnode . state === 3 ) return ;
611+
612+ const { queues } = vnode ;
613+ const { updates } = queues ;
614+
615+ if ( updates . size === 0 ) {
616+ queueMicrotask ( ( ) => {
617+ const batch = [ ...updates ] . sort ( batchComparator ) ;
618+ updates . clear ( ) ;
619+ for ( const vnode of batch ) if ( vnode . state === 1 ) update ( vnode ) ;
620+ flush ( queues ) ;
621+ } ) ;
622+ }
623+ updates . add ( vnode ) ;
624+ vnode . state = 1 ;
625+ } ;
626+
604627/**
605628 * @template T
606629 * @param {T | (() => T) } initial
@@ -617,26 +640,13 @@ export const useState = initial => {
617640 queueUpdate ( vnode ) ;
618641 }
619642 return value ;
620- }
643+ } ,
644+ maybeValue =>
645+ ( state [ 0 ] = isFunction ( maybeValue ) ? maybeValue ( state [ 0 ] ) : maybeValue )
621646 ] ) ;
622647 return state ;
623648} ;
624649
625- /**
626- * @param {unknown[] } before
627- * @param {unknown[] } after
628- */
629- const depsChanged = ( before , after ) => {
630- if ( before === after ) return false ;
631-
632- let { length } = before ;
633- if ( length !== after . length ) return true ;
634-
635- while ( length -- ) if ( before [ length ] !== after [ length ] ) return true ;
636-
637- return false ;
638- } ;
639-
640650/**
641651 * @template {AnyFunction} T
642652 * @param {T } fn
@@ -649,30 +659,6 @@ export const useCallback = fn => {
649659 ) ;
650660} ;
651661
652- /**
653- * @param {Props } prev
654- * @param {Props } next
655- */
656- const defaultMemo = ( prev , next ) => {
657- if ( prev === next ) return true ;
658-
659- for ( const key in prev ) if ( prev [ key ] !== next [ key ] ) return false ;
660-
661- for ( const key in next ) if ( ! ( key in prev ) ) return false ;
662-
663- return true ;
664- } ;
665-
666- /**
667- * @template {Component} T
668- * @param {T } Component
669- * @param {typeof defaultMemo } [memo]
670- */
671- export const memo = ( Component , memo = defaultMemo ) => {
672- Component . memo = memo ;
673- return Component ;
674- } ;
675-
676662/**
677663 * @template {Context<any>} T
678664 * @param {T } Context
@@ -692,48 +678,6 @@ export const useContext = Context => {
692678 return /** @type {T['value'] } */ ( ( context ?? Context ) . value ) ;
693679} ;
694680
695- /**
696- * @param {Vnode } a
697- * @param {Vnode } b
698- */
699- const batchComparator = ( a , b ) => {
700- while ( a . depth > b . depth ) {
701- a = /** @type {Vnode } */ ( a . parent ) ;
702- if ( a === b ) return 1 ;
703- }
704-
705- while ( b . depth > a . depth ) {
706- b = /** @type {Vnode } */ ( b . parent ) ;
707- if ( b === a ) return - 1 ;
708- }
709-
710- while ( a . parent !== b . parent ) {
711- a = /** @type {Vnode } */ ( a . parent ) ;
712- b = /** @type {Vnode } */ ( b . parent ) ;
713- }
714-
715- return a . index - b . index ;
716- } ;
717-
718- /** @param {Vnode } vnode */
719- const queueUpdate = vnode => {
720- if ( vnode . state === 1 || vnode . state === 3 ) return ;
721-
722- const { queues } = vnode ;
723- const { updates } = queues ;
724-
725- if ( updates . length === 0 ) {
726- queueMicrotask ( ( ) => {
727- const batch = updates . sort ( batchComparator ) ;
728- queues . updates = [ ] ;
729- for ( const vnode of batch ) if ( vnode . state === 1 ) update ( vnode ) ;
730- flush ( queues ) ;
731- } ) ;
732- }
733- updates . push ( vnode ) ;
734- vnode . state = 1 ;
735- } ;
736-
737681/** @param {Vnode } vnode */
738682const getDefs = vnode => {
739683 const { props, type } = vnode ;
@@ -778,6 +722,64 @@ const normalizeDef = def => {
778722 return jsx ( textType , { nodeValue : def } ) ;
779723} ;
780724
725+ /**
726+ * @template {Element} Node
727+ * @param {Node } node
728+ * @param {keyof Node } key
729+ */
730+ const isSimpleProperty = ( node , key ) =>
731+ key in node && ( node [ key ] == null || ! isObject ( node [ key ] ) ) ;
732+
733+ /**
734+ * @template {Element | Text} T
735+ * @param {T } node
736+ * @param {Props<T> } prev
737+ * @param {Props<T> } next
738+ */
739+ const updateNode = ( node , prev , next ) => {
740+ if ( prev === next ) return ;
741+
742+ // Element.prototype.TEXT_NODE
743+ if ( node . nodeType === 3 ) {
744+ node . nodeValue = /** @type {string } */ ( next . nodeValue ) ;
745+ return ;
746+ }
747+
748+ for ( const key in prev ) {
749+ if ( key === 'children' || key in next ) continue ;
750+
751+ if ( key === 'ref' ) {
752+ if ( prev . ref ) prev . ref . current = null ;
753+ // @ts -expect-error assignment of a readonly property would fail
754+ } else if ( isSimpleProperty ( node , key ) ) node [ key ] = '' ;
755+ else /** @type {Element } */ ( node ) . removeAttribute ( key ) ;
756+ }
757+
758+ for ( const key in next ) {
759+ if ( key === 'children' || prev [ key ] === next [ key ] ) continue ;
760+
761+ if ( key === 'ref' ) {
762+ if ( prev . ref ) prev . ref . current = null ;
763+ if ( next . ref ) next . ref . current = node ;
764+ } else if ( key === 'style' && isObject ( next [ key ] ) ) {
765+ const { style } = /** @type {HTMLElement | SVGElement } */ ( node ) ;
766+ const prevStyle = /** @type {Partial<CSSStyleDeclaration> } */ (
767+ isObject ( prev [ key ] ) ? prev [ key ] : emptyObject
768+ ) ;
769+ const nextStyle = /** @type {Partial<CSSStyleDeclaration> } */ ( next [ key ] ) ;
770+ for ( const key in prevStyle ) if ( ! ( key in nextStyle ) ) style [ key ] = '' ;
771+ for ( const key in nextStyle ) style [ key ] = nextStyle [ key ] ?? '' ;
772+ // @ts -expect-error assignment of a readonly property would fail
773+ } else if ( isSimpleProperty ( node , key ) ) node [ key ] = next [ key ] ?? '' ;
774+ else if ( next [ key ] != null ) {
775+ /** @type {Element } */ ( node ) . setAttribute (
776+ key ,
777+ /** @type {string } */ ( next [ key ] )
778+ ) ;
779+ } else /** @type {Element } */ ( node ) . removeAttribute ( key ) ;
780+ }
781+ } ;
782+
781783/** @param {Vnode } vnode */
782784const updateChild = vnode => {
783785 let { child, state } = vnode ;
@@ -1015,7 +1017,7 @@ export const createRender = parentNode => {
10151017 inserts : [ ] ,
10161018 nodeUpdates : [ ] ,
10171019 removes : [ ] ,
1018- updates : [ ]
1020+ updates : new Set ( )
10191021 } ,
10201022 refs : null ,
10211023 sibling : null ,
0 commit comments