|
1 | 1 | (ns reagent.hooks |
2 | 2 | (:require ["react" :as react])) |
3 | 3 |
|
| 4 | +;; Helpers to make hooks use clojure value equality etc. |
| 5 | +;; These are copied from (with permission) or inspired by UIx implementation |
| 6 | + |
| 7 | +(defn with-return-value-check |
| 8 | + "Consider any non-fn value from effect callbacks as no callback." |
| 9 | + [f] |
| 10 | + (fn [] |
| 11 | + (let [ret (f)] |
| 12 | + (if (fn? ret) |
| 13 | + ret |
| 14 | + js/undefined)))) |
| 15 | + |
| 16 | +(defn use-clj-deps |
| 17 | + "Check the new clj deps value against the previous value, stored in a |
| 18 | + ref hook, so we can use clj equality check to see if the value changed. |
| 19 | +
|
| 20 | + Only not= value is stored to the ref, so the effect dependency value |
| 21 | + only updates when the equality changed." |
| 22 | + [deps] |
| 23 | + (let [ref (react/useRef deps)] |
| 24 | + (when (not= (.-current ref) deps) |
| 25 | + (set! (.-current ref) deps)) |
| 26 | + (.-current ref))) |
| 27 | + |
| 28 | +(defn- use-clojure-aware-updater |
| 29 | + "When update fn returns the equal clj value as the previous value, |
| 30 | + keep using the old value, to keep the identity stable." |
| 31 | + [updater] |
| 32 | + (react/useCallback |
| 33 | + (fn [v & args] |
| 34 | + (updater |
| 35 | + (fn [current-value] |
| 36 | + (let [new-value (if (fn? v) |
| 37 | + (apply v current-value args) |
| 38 | + v)] |
| 39 | + (js/console.log (pr-str current-value) (pr-str new-value) (= new-value current-value)) |
| 40 | + (if (= new-value current-value) |
| 41 | + current-value |
| 42 | + new-value))))) |
| 43 | + #js [updater])) |
| 44 | + |
| 45 | +;; Hooks |
| 46 | + |
4 | 47 | (defn use-state [initial-state] |
5 | | - (react/useState initial-state)) |
| 48 | + (let [[state set-state] (react/useState initial-state) |
| 49 | + set-state (use-clojure-aware-updater set-state)] |
| 50 | + #js [state set-state])) |
6 | 51 |
|
7 | 52 | (defn use-reducer [reducer initial-arg init] |
8 | | - (react/useReducer reducer initial-arg init)) |
| 53 | + (react/useReducer (fn [state action] |
| 54 | + (let [new-state (reducer state action)] |
| 55 | + (if (= new-state state) |
| 56 | + state |
| 57 | + new-state))) |
| 58 | + initial-arg |
| 59 | + init)) |
9 | 60 |
|
10 | 61 | (defn use-ref [v] |
11 | 62 | (react/useRef v)) |
12 | 63 |
|
13 | 64 | (defn use-effect [setup dependencies] |
14 | | - (react/useEffect setup dependencies)) |
| 65 | + (react/useEffect (with-return-value-check setup) |
| 66 | + (array (use-clj-deps dependencies)))) |
15 | 67 |
|
16 | 68 | (defn use-layout-effect [setup dependencies] |
17 | | - (react/useLayoutEffect setup dependencies)) |
| 69 | + (react/useLayoutEffect (with-return-value-check setup) |
| 70 | + (array (use-clj-deps dependencies)))) |
18 | 71 |
|
19 | 72 | (defn use-memo [calculate-value dependencies] |
20 | | - (react/useMemo calculate-value dependencies)) |
| 73 | + (react/useMemo calculate-value |
| 74 | + (array (use-clj-deps dependencies)))) |
21 | 75 |
|
22 | 76 | (defn use-callback [f dependencies] |
23 | | - (react/useCallback f dependencies)) |
| 77 | + (react/useCallback f |
| 78 | + (array (use-clj-deps dependencies)))) |
24 | 79 |
|
25 | 80 | (defn use-context [ctx] |
26 | 81 | (react/useContext ctx)) |
0 commit comments