소스 검색

React 18 friendly useBehavior hook

dsehnal 3 년 전
부모
커밋
f040c89ab3
2개의 변경된 파일33개의 추가작업 그리고 10개의 파일을 삭제
  1. 1 0
      CHANGELOG.md
  2. 32 10
      src/mol-plugin-ui/hooks/use-behavior.ts

+ 1 - 0
CHANGELOG.md

@@ -7,6 +7,7 @@ Note that since we don't clearly distinguish between a public and private interf
 ## [Unreleased]
 ## [Unreleased]
 
 
 - ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
 - ModelServer ligand queries: fixes for alternate locations, additional atoms & UNL ligand
+- React 18 friendly ``useBehavior`` hook.
 
 
 ## [v3.6.1] - 2022-04-03
 ## [v3.6.1] - 2022-04-03
 
 

+ 32 - 10
src/mol-plugin-ui/hooks/use-behavior.ts

@@ -1,26 +1,23 @@
 /**
 /**
- * Copyright (c) 2020-21 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2020-22 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author David Sehnal <david.sehnal@gmail.com>
  */
  */
 
 
-import { useEffect, useRef, useState } from 'react';
+import React from 'react';
+import { skip } from 'rxjs';
 
 
 interface Behavior<T> {
 interface Behavior<T> {
     value: T;
     value: T;
     subscribe(f: (v: T) => void): { unsubscribe(): void };
     subscribe(f: (v: T) => void): { unsubscribe(): void };
 }
 }
 
 
-export function useBehavior<T>(s: Behavior<T>): T;
+function useBehaviorLegacy<T>(s: Behavior<T> | undefined): T | undefined {
-// eslint-disable-next-line
+    const [, next] = React.useState({});
-export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined;
+    const current = React.useRef<T>();
-// eslint-disable-next-line
-export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
-    const [, next] = useState({});
-    const current = useRef<T>();
     current.current = s?.value;
     current.current = s?.value;
 
 
-    useEffect(() => {
+    React.useEffect(() => {
         if (!s) {
         if (!s) {
             return;
             return;
         }
         }
@@ -32,4 +29,29 @@ export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
     }, [s]);
     }, [s]);
 
 
     return s?.value;
     return s?.value;
+}
+
+function useBehaviorReact18<T>(s: Behavior<T> | undefined) {
+    return (React as any).useSyncExternalStore(
+        React.useCallback(
+            (callback: () => void) => {
+                const sub = (s as any)?.pipe!(skip(1)).subscribe(callback)!;
+                return () => sub?.unsubscribe();
+            },
+            [s]
+        ),
+        React.useCallback(() => s?.value, [s])
+    );
+}
+
+const _useBehavior = !!(React as any).useSyncExternalStore
+    ? useBehaviorReact18
+    : useBehaviorLegacy;
+
+export function useBehavior<T>(s: Behavior<T>): T;
+// eslint-disable-next-line
+export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined;
+// eslint-disable-next-line
+export function useBehavior<T>(s: Behavior<T> | undefined): T | undefined {
+    return _useBehavior(s);
 }
 }