Skip to content

Commit ddac7fe

Browse files
updated the effect cleanup logic to be more predictable and easy to reason && some readme changes
1 parent 20b795f commit ddac7fe

File tree

8 files changed

+57
-33
lines changed

8 files changed

+57
-33
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "refract-js",
33
"type": "module",
4-
"version": "1.0.0",
4+
"version": "1.1.0",
55
"description": "Simpler, Faster and Lighter JSX based rendering for Single-Page Applications",
66
"main": "build/refract.cjs.js",
77
"module": "build/refract.es.js",

readme.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export default Counter;
7878

7979
### Effects
8080

81+
Effects can be created using the `createEffect` function.
82+
83+
- Effect runs after the components has been mounted to the dom.
84+
- You don't need to declare dependency array as dependencies are automatically tracked.
85+
- Effect re-runs whenever the signals used inside the effect are updated using the update function.
86+
- The cleanup function that is returned from the effect will run when the component unmounts or when the effect is rerun.
87+
8188
```jsx
8289
import { createSignal, createEffect } from "refract-js";
8390

@@ -86,13 +93,21 @@ function TimerComponent() {
8693

8794
createEffect(() => {
8895
const interval = setInterval(() => {
89-
seconds.value++;
96+
seconds.update((prev) => prev + 1);
9097
}, 1000);
9198

92-
// Cleanup function to clear the interval when the component unmounts
99+
// Cleanup function to clear the interval when the component unmounts or the seconds.value is updated
93100
return () => clearInterval(interval);
94101
});
95102

103+
createEffect(() => {
104+
// This effect only runs once after the component mounts.
105+
106+
return () => {
107+
// this cleanup will only run when the component mounts
108+
};
109+
});
110+
96111
return <p>Elapsed Time: {seconds} seconds</p>;
97112
}
98113
```

src/components/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const App = (props: any) => {
1616
<button onClick={() => visible.update((prev) => !prev)}>
1717
Show/Hide
1818
</button>
19-
{() => (visible.value ? <PerformanceTest /> : "Hidden")}
19+
{() => (visible.value ? <Test /> : "Hidden")}
2020
</>
2121
);
2222
// return <StylesTest />;

src/components/Test.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,28 @@ import {
99
const LazyFC1 = lazy(() => import("./FC1.jsx"));
1010
const LazyFC2 = lazy(() => import("./FC2.jsx"));
1111

12-
const showTextSignal = createSignal<boolean>(true);
1312
export const textSignal = createSignal<string>("Initial Text");
1413

1514
const Test = () => {
15+
const showTextSignal = createSignal<boolean>(true);
1616
createEffect(() => {
17-
console.log(showTextSignal.value);
17+
console.log(showTextSignal.value, "Effect");
18+
19+
return () => {
20+
console.log("cleanup");
21+
};
1822
});
1923

2024
const h1ref = createRef<HTMLHeadingElement>();
21-
25+
// console.log("hello");
2226
return (
2327
<div>
2428
{/* Static content with reactivity */}
2529
<h1 ref={h1ref}>
2630
<>{() => textSignal.value}</>
2731
</h1>
2832

29-
{() =>
33+
{/* {() =>
3034
showTextSignal.value ? (
3135
<LazyFC2
3236
fallback={<h2>Loading...</h2>}
@@ -38,17 +42,15 @@ const Test = () => {
3842
errorFallback={(error) => <h2>{error.message}</h2>}
3943
/>
4044
)
41-
}
42-
<button
43-
onClick={() => (showTextSignal.value = !showTextSignal.value)}
44-
>
45+
} */}
46+
<button onClick={() => showTextSignal.update((prev) => !prev)}>
4547
Toggle
4648
</button>
4749

48-
{/* Event handlers with reactivity */}
50+
{/* Event handlers with reactivity
4951
<button onClick={() => (textSignal.value = "Updated Text")}>
5052
Update Text
51-
</button>
53+
</button> */}
5254
</div>
5355
);
5456
};

src/rendering/functionalComponents.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ export function cleanUpFC(currentFC, props) {
111111
fcData.cleanup = [];
112112

113113
for (const effect of fcData.effects) {
114+
// @ts-expect-error
115+
if (effect.__cleanup && typeof effect.__cleanup === "function") {
116+
// @ts-expect-error
117+
effect.__cleanup();
118+
}
114119
// @ts-expect-error
115120
if (effect.__signals) {
116121
// @ts-expect-error
@@ -120,6 +125,8 @@ export function cleanUpFC(currentFC, props) {
120125
}
121126
// @ts-expect-error
122127
delete effect.__signals;
128+
// @ts-expect-error
129+
delete effect.__cleanup;
123130
}
124131

125132
fcData.signals.forEach((signal) => signal.clearDeps());

src/rendering/render.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,15 @@ export function render(element: Fiber, container: HTMLElement) {
4242
function commitRootFragment() {
4343
if (rootFragment && rootContainer) {
4444
rootContainer.appendChild(rootFragment);
45-
const endTime = performance.now();
46-
console.log(`Render time: ${endTime - startTime}ms`);
45+
// const endTime = performance.now();
46+
// console.log(`Render time: ${endTime - startTime}ms`);
4747
}
4848
}
4949

5050
let elements: Fiber[] = [];
5151
let rootContainer: HTMLElement | null = null;
5252
let rootFragment: DocumentFragment | null = null;
53-
let startTime = -1;
53+
// let startTime = -1;
5454
let effectQueue: Fiber[] = [];
5555

5656
function processEffectQueue() {
@@ -62,7 +62,7 @@ function processEffectQueue() {
6262
}
6363

6464
function workLoop(deadline: IdleDeadline) {
65-
if (startTime === -1) startTime = performance.now();
65+
// if (startTime === -1) startTime = performance.now();
6666

6767
processEffectQueue();
6868
let shouldYield = false;
@@ -293,7 +293,7 @@ function setRenderFunction(fiber: Fiber) {
293293

294294
export function updateFiber(prevFiber: Fiber, newValue) {
295295
// console.log("Prev value", prevFiber, newValue);
296-
startTime = performance.now();
296+
// startTime = performance.now();
297297
if (isPrimitive(newValue)) {
298298
// console.log(fiber, newValue);
299299
const newFragment: Fiber = {
@@ -323,8 +323,8 @@ export function updateFiber(prevFiber: Fiber, newValue) {
323323
// console.log("New Node Fiber", newFragment);
324324
updateNode(prevFiber, newFragment);
325325
}
326-
const endTime = performance.now();
327-
console.log("Update Time:", (endTime - startTime).toFixed(2), "ms");
326+
// const endTime = performance.now();
327+
// console.log("Update Time:", (endTime - startTime).toFixed(2), "ms");
328328
}
329329

330330
function replaceRenderFunction(prev: Fiber, next: Fiber) {
@@ -525,7 +525,7 @@ function updateNode(
525525
)
526526
next.dom = prev.dom;
527527
if (node === undefined) {
528-
console.error("no node found", prev, next);
528+
// console.error("no node found", prev, next);
529529
return;
530530
}
531531
// console.log(prev);

src/signals/batch.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,30 @@ const batch = new Set<Function>();
77
const depset = new Set();
88
const reactiveFiberMap = new WeakMap();
99
const domAttributeMap = new WeakMap<Function, HTMLElement | Text>();
10-
const effectCleanup: any[] = [];
11-
12-
export function addEffectCleanup(fn: Function) {
13-
effectCleanup.push(fn);
14-
}
1510

1611
export function batchUpdate(cb: Function) {
1712
batch.add(cb);
1813
if (!scheduled) {
1914
scheduled = true;
2015
queueMicrotask(() => {
2116
// console.log("Current batch has: ", batch.size, " Functions");
22-
effectCleanup.forEach((fn) => fn());
23-
effectCleanup.length = 0;
17+
2418
batch.forEach((fn) => {
2519
const dep = fn();
2620
if (depset.has(dep)) {
2721
return;
2822
}
2923
depset.add(dep);
3024
// effects and reactive nodes
25+
if (dep.__cleanup && typeof dep.__cleanup === "function") {
26+
dep.__cleanup();
27+
dep.__cleanup = null;
28+
}
29+
3130
const val = dep();
3231

3332
if (typeof val === "function") {
34-
effectCleanup.push(val);
33+
dep.__cleanup = val;
3534
}
3635
// console.log(dep, "dep");
3736
if (reactiveFiberMap.has(dep)) {

src/signals/signal.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
} from "../rendering/functionalComponents";
88
import { Fiber } from "../types";
99
import { isPlainObject, isPrimitive } from "../utils/general";
10-
import { addEffectCleanup, batchUpdate } from "./batch";
10+
import { batchUpdate } from "./batch";
1111

1212
let currentReactiveFunction: any = null;
1313
let currentEffect: any = null;
@@ -68,8 +68,9 @@ export function runEffect(effect: Function, fiber?: Fiber) {
6868

6969
const effectCleanup = effect();
7070

71-
if (currentEffect.__signals && typeof effectCleanup === "function")
72-
addEffectCleanup(effectCleanup);
71+
if (currentEffect.__signals && typeof effectCleanup === "function") {
72+
currentEffect.__cleanup = effectCleanup;
73+
}
7374

7475
if (
7576
!currentEffect.__signals &&

0 commit comments

Comments
 (0)