|
1 | | -import { Component, useState, useEffect, useMemo, memo } from 'react'; |
| 1 | +import { |
| 2 | + Component, |
| 3 | + useState, |
| 4 | + useEffect, |
| 5 | + useMemo, |
| 6 | + memo, |
| 7 | + useRef, |
| 8 | +} from 'react'; |
2 | 9 | import { |
3 | 10 | observe, |
4 | 11 | unobserve, |
@@ -36,22 +43,43 @@ export function view(Comp) { |
36 | 43 | ReactiveComp = props => { |
37 | 44 | // use a dummy setState to update the component |
38 | 45 | const [, setState] = useState(); |
| 46 | + // use a ref to store the reaction |
| 47 | + const reaction = useRef(); |
39 | 48 | // create a memoized reactive wrapper of the original component (render) |
40 | 49 | // at the very first run of the component function |
41 | 50 | const render = useMemo( |
42 | | - () => |
43 | | - observe(Comp, { |
44 | | - scheduler: () => setState({}), |
| 51 | + () => { |
| 52 | + reaction.current = observe(Comp, { |
| 53 | + scheduler: () => { |
| 54 | + // trigger a new rerender if the component has been mounted |
| 55 | + if (reaction.current.mounted) setState({}); |
| 56 | + // mark it as changed if the component has not been mounted yet |
| 57 | + else reaction.current.changedBeforeMounted = true; |
| 58 | + }, |
45 | 59 | lazy: true, |
46 | | - }), |
| 60 | + }); |
| 61 | + // initilalize a flag to know if the component was finally mounted |
| 62 | + reaction.current.mounted = false; |
| 63 | + // initilalize a flag to know if the was reaction was invalidated |
| 64 | + // before the component was mounted |
| 65 | + reaction.current.changedBeforeMounted = false; |
| 66 | + return reaction.current; |
| 67 | + }, |
47 | 68 | // Adding the original Comp here is necessary to make React Hot Reload work |
48 | 69 | // it does not affect behavior otherwise |
49 | 70 | [Comp], |
50 | 71 | ); |
51 | 72 |
|
52 | | - // cleanup the reactive connections after the very last render of the component |
53 | 73 | useEffect(() => { |
54 | | - return () => unobserve(render); |
| 74 | + // mark the component as mounted. |
| 75 | + reaction.current.mounted = true; |
| 76 | + |
| 77 | + // if there was a change before the component was mounted, trigger a |
| 78 | + // new rerender |
| 79 | + if (reaction.current.changedBeforeMounted) setState({}); |
| 80 | + |
| 81 | + // cleanup the reactive connections after the very last render of the |
| 82 | + return () => unobserve(reaction.current); |
55 | 83 | }, []); |
56 | 84 |
|
57 | 85 | // the isInsideFunctionComponent flag is used to toggle `store` behavior |
|
0 commit comments