Skip to content

Commit 279d18f

Browse files
luisherranzrolandszoke
authored andcommitted
fix(view): don't rerender unmounted components
1 parent 41c5384 commit 279d18f

1 file changed

Lines changed: 35 additions & 7 deletions

File tree

src/view.js

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
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';
29
import {
310
observe,
411
unobserve,
@@ -36,22 +43,43 @@ export function view(Comp) {
3643
ReactiveComp = props => {
3744
// use a dummy setState to update the component
3845
const [, setState] = useState();
46+
// use a ref to store the reaction
47+
const reaction = useRef();
3948
// create a memoized reactive wrapper of the original component (render)
4049
// at the very first run of the component function
4150
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+
},
4559
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+
},
4768
// Adding the original Comp here is necessary to make React Hot Reload work
4869
// it does not affect behavior otherwise
4970
[Comp],
5071
);
5172

52-
// cleanup the reactive connections after the very last render of the component
5373
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);
5583
}, []);
5684

5785
// the isInsideFunctionComponent flag is used to toggle `store` behavior

0 commit comments

Comments
 (0)