Skip to content

Commit

Permalink
fix #1821 improve context performance
Browse files Browse the repository at this point in the history
  • Loading branch information
ryansolid committed Aug 9, 2023
1 parent c2008f0 commit 792e7de
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .changeset/young-months-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"solid-js": patch
---

fix #1821 improve context performance
90 changes: 50 additions & 40 deletions packages/solid/src/reactive/signal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,15 +140,16 @@ export function createRoot<T>(fn: RootFunction<T>, detachedOwner?: typeof Owner)
const listener = Listener,
owner = Owner,
unowned = fn.length === 0,
current = detachedOwner === undefined ? owner : detachedOwner,
root: Owner = unowned
? "_SOLID_DEV_"
? { owned: null, cleanups: null, context: null, owner: null }
: UNOWNED
: {
owned: null,
cleanups: null,
context: null,
owner: detachedOwner === undefined ? owner : detachedOwner
context: current ? current.context : null,
owner: current
},
updateFn = unowned
? "_SOLID_DEV_"
Expand Down Expand Up @@ -347,7 +348,7 @@ export function createEffect<Next, Init>(
): void {
runEffects = runUserEffects;
const c = createComputation(fn, value!, false, STALE, "_SOLID_DEV_" ? options : undefined),
s = SuspenseContext && lookup(Owner, SuspenseContext.id);
s = SuspenseContext && useContext(SuspenseContext);
if (s) c.suspense = s;
if (!options || !options.render) c.user = true;
Effects ? Effects.push(c) : updateComputation(c);
Expand Down Expand Up @@ -378,7 +379,7 @@ export function createReaction(onInvalidate: () => void, options?: EffectOptions
0,
"_SOLID_DEV_" ? options : undefined
),
s = SuspenseContext && lookup(Owner, SuspenseContext.id);
s = SuspenseContext && useContext(SuspenseContext);
if (s) c.suspense = s;
c.user = true;
return (tracking: () => void) => {
Expand Down Expand Up @@ -646,7 +647,7 @@ export function createResource<T, S, R>(
}

function read() {
const c = SuspenseContext && lookup(Owner, SuspenseContext.id),
const c = SuspenseContext && useContext(SuspenseContext),
v = value(),
err = error();
if (err !== undefined && !pr) throw err;
Expand All @@ -656,7 +657,7 @@ export function createResource<T, S, R>(
if (pr) {
if (c.resolved && Transition && loadedUnderTransition) Transition.promises.add(pr);
else if (!contexts.has(c)) {
c.increment();
c.increment!();
contexts.add(c);
}
}
Expand Down Expand Up @@ -981,7 +982,7 @@ export function onCleanup<T extends () => any>(fn: T): T {
export function catchError<T>(fn: () => T, handler: (err: Error) => void) {
ERROR || (ERROR = Symbol("error"));
Owner = createComputation(undefined!, undefined, true);
Owner.context = { [ERROR]: [handler] };
Owner.context = { ...Owner.context, [ERROR]: [handler] };
if (Transition && Transition.running) Transition.sources.add(Owner as Memo<any>);
try {
return fn();
Expand All @@ -992,25 +993,6 @@ export function catchError<T>(fn: () => T, handler: (err: Error) => void) {
}
}

/**
* @deprecated since version 1.7.0 and will be removed in next major - use catchError instead
* onError - run an effect whenever an error is thrown within the context of the child scopes
* @param fn an error handler that receives the error
*
* * If the error is thrown again inside the error handler, it will trigger the next available parent handler
*
* @description https://www.solidjs.com/docs/latest/api#onerror
*/
export function onError(fn: (err: Error) => void): void {
ERROR || (ERROR = Symbol("error"));
if (Owner === null)
"_SOLID_DEV_" &&
console.warn("error handlers created outside a `createRoot` or `render` will never be run");
else if (Owner.context === null) Owner.context = { [ERROR]: [fn] };
else if (!Owner.context[ERROR]) Owner.context[ERROR] = [fn];
else Owner.context[ERROR].push(fn);
}

export function getListener() {
return Listener;
}
Expand Down Expand Up @@ -1181,8 +1163,9 @@ export function createContext<T>(
* @description https://www.solidjs.com/docs/latest/api#usecontext
*/
export function useContext<T>(context: Context<T>): T {
let ctx;
return (ctx = lookup(Owner, context.id)) !== undefined ? ctx : context.defaultValue;
return Owner && Owner.context && Owner.context[context.id] !== undefined
? Owner.context[context.id]
: context.defaultValue;
}

export type ResolvedJSXElement = Exclude<JSX.Element, JSX.ArrayElement>;
Expand Down Expand Up @@ -1401,7 +1384,7 @@ function createComputation<Next, Init = unknown>(
cleanups: null,
value: init,
owner: Owner,
context: null,
context: Owner ? Owner.context : null,
pure
};

Expand Down Expand Up @@ -1659,7 +1642,6 @@ function cleanNode(node: Owner) {
}
if (Transition && Transition.running) (node as Computation<any>).tState = 0;
else (node as Computation<any>).state = 0;
node.context = null;
"_SOLID_DEV_" && delete node.sourceMap;
}

Expand Down Expand Up @@ -1687,7 +1669,7 @@ function runErrors(err: unknown, fns: ((err: any) => void)[], owner: Owner | nul
}

function handleError(err: unknown, owner = Owner) {
const fns = ERROR && lookup(owner, ERROR);
const fns = ERROR && owner && owner.context && owner.context[ERROR];
const error = castError(err);
if (!fns) throw error;

Expand All @@ -1701,14 +1683,6 @@ function handleError(err: unknown, owner = Owner) {
else runErrors(error, fns, owner);
}

function lookup(owner: Owner | null, key: symbol | string): any {
return owner
? owner.context && owner.context[key] !== undefined
? owner.context[key]
: lookup(owner.owner, key)
: undefined;
}

function resolveChildren(children: JSX.Element | Accessor<any>): ResolvedChildren {
if (typeof children === "function" && !children.length) return resolveChildren(children());
if (Array.isArray(children)) {
Expand All @@ -1728,7 +1702,7 @@ function createProvider(id: symbol, options?: EffectOptions) {
createRenderEffect(
() =>
(res = untrack(() => {
Owner!.context = { [id]: props.value };
Owner!.context = { ...Owner!.context, [id]: props.value };
return children(() => props.children);
})),
undefined,
Expand All @@ -1739,3 +1713,39 @@ function createProvider(id: symbol, options?: EffectOptions) {
}

type TODO = any;

/**
* @deprecated since version 1.7.0 and will be removed in next major - use catchError instead
* onError - run an effect whenever an error is thrown within the context of the child scopes
* @param fn an error handler that receives the error
*
* * If the error is thrown again inside the error handler, it will trigger the next available parent handler
*
* @description https://www.solidjs.com/docs/latest/api#onerror
*/
export function onError(fn: (err: Error) => void): void {
ERROR || (ERROR = Symbol("error"));
if (Owner === null)
"_SOLID_DEV_" &&
console.warn("error handlers created outside a `createRoot` or `render` will never be run");
else if (Owner.context === null || !Owner.context[ERROR]) {
// terrible de-opt
Owner.context = { ...Owner.context, [ERROR]: [fn] };
mutateContext(Owner, ERROR, [fn]);
} else Owner.context[ERROR].push(fn);
}

function mutateContext(o: Owner, key: symbol, value: any) {
if (o.owned) {
for (let i = 0; i < o.owned.length; i++) {
if (o.owned[i].context === o.context) mutateContext(o.owned[i], key, value);
if (!o.owned[i].context) {
o.owned[i].context = o.context;
mutateContext(o.owned[i], key, value);
} else if (!o.owned[i].context[key]) {
o.owned[i].context[key] = value;
mutateContext(o.owned[i], key, value);
}
}
}
}
1 change: 1 addition & 0 deletions packages/solid/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export {
catchError,
createRoot,
createSignal,
createComputed,
Expand Down
67 changes: 40 additions & 27 deletions packages/solid/src/server/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function castError(err: unknown): Error {
}

function handleError(err: unknown, owner = Owner): void {
const fns = lookup(owner, ERROR);
const fns = owner && owner.context && owner.context[ERROR];
const error = castError(err);
if (!fns) throw error;

Expand All @@ -41,7 +41,7 @@ interface Owner {
}

export function createOwner(): Owner {
const o = { owner: Owner, context: null, owned: null, cleanups: null };
const o = { owner: Owner, context: Owner ? Owner.context : null, owned: null, cleanups: null };
if (Owner) {
if (!Owner.owned) Owner.owned = [o];
else Owner.owned.push(o);
Expand All @@ -51,12 +51,13 @@ export function createOwner(): Owner {

export function createRoot<T>(fn: (dispose: () => void) => T, detachedOwner?: typeof Owner): T {
const owner = Owner,
current = detachedOwner === undefined ? owner : detachedOwner,
root =
fn.length === 0
? UNOWNED
: {
context: null,
owner: detachedOwner === undefined ? owner : detachedOwner,
context: current ? current.context : null,
owner: current,
owned: null,
cleanups: null
};
Expand Down Expand Up @@ -172,7 +173,9 @@ export function cleanNode(node: Owner) {
}

export function catchError<T>(fn: () => T, handler: (err: Error) => void) {
Owner = { owner: Owner, context: { [ERROR]: [handler] }, owned: null, cleanups: null };
const owner = createOwner();
owner.context = { ...owner.context, [ERROR]: [handler] };
Owner = owner;
try {
return fn();
} catch (err) {
Expand All @@ -182,17 +185,6 @@ export function catchError<T>(fn: () => T, handler: (err: Error) => void) {
}
}

/**
* @deprecated since version 1.7.0 and will be removed in next major - use catchError instead
*/
export function onError(fn: (err: Error) => void): void {
if (Owner) {
if (Owner.context === null) Owner.context = { [ERROR]: [fn] };
else if (!Owner.context[ERROR]) Owner.context[ERROR] = [fn];
else Owner.context[ERROR].push(fn);
}
}

export function getListener() {
return null;
}
Expand All @@ -210,8 +202,9 @@ export function createContext<T>(defaultValue?: T): Context<T> {
}

export function useContext<T>(context: Context<T>): T {
let ctx;
return (ctx = lookup(Owner, context.id)) !== undefined ? ctx : context.defaultValue;
return Owner && Owner.context && Owner.context[context.id] !== undefined
? Owner.context[context.id]
: context.defaultValue;
}

export function getOwner() {
Expand Down Expand Up @@ -240,14 +233,6 @@ export function runWithOwner<T>(o: typeof Owner, fn: () => T): T | undefined {
}
}

export function lookup(owner: Owner | null, key: symbol | string): any {
return owner
? owner.context && owner.context[key] !== undefined
? owner.context[key]
: lookup(owner.owner, key)
: undefined;
}

function resolveChildren(children: any): unknown {
if (typeof children === "function" && !children.length) return resolveChildren(children());
if (Array.isArray(children)) {
Expand All @@ -264,7 +249,7 @@ function resolveChildren(children: any): unknown {
function createProvider(id: symbol) {
return function provider(props: { value: unknown; children: any }) {
return createMemo<JSX.Element>(() => {
Owner!.context = { [id]: props.value };
Owner!.context = { ...Owner!.context, [id]: props.value };
return children(() => props.children) as unknown as JSX.Element;
});
};
Expand Down Expand Up @@ -365,3 +350,31 @@ export function from<T>(
}

export function enableExternalSource(factory: any) {}

/**
* @deprecated since version 1.7.0 and will be removed in next major - use catchError instead
*/
export function onError(fn: (err: Error) => void): void {
if (Owner) {
if (Owner.context === null || !Owner.context[ERROR]) {
// terrible de-opt
Owner.context = { ...Owner.context, [ERROR]: [fn] };
mutateContext(Owner, ERROR, [fn]);
} else Owner.context[ERROR].push(fn);
}
}

function mutateContext(o: Owner, key: symbol, value: any) {
if (o.owned) {
for (let i = 0; i < o.owned.length; i++) {
if (o.owned[i].context === o.context) mutateContext(o.owned[i], key, value);
if (!o.owned[i].context) {
o.owned[i].context = o.context;
mutateContext(o.owned[i], key, value);
} else if (!o.owned[i].context[key]) {
o.owned[i].context[key] = value;
mutateContext(o.owned[i], key, value);
}
}
}
}

0 comments on commit 792e7de

Please sign in to comment.