Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/orange-ants-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'svelte': minor
---

feat: allow use of createContext when instantiating components programmatically
26 changes: 26 additions & 0 deletions documentation/docs/06-runtime/02-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,32 @@ import { createContext } from 'svelte';
export const [getUserContext, setUserContext] = createContext<User>();
```

When writing [component tests](testing#Unit-and-component-tests-with-Vitest-Component-testing), it can be useful to create a wrapper component that sets the context in order to check the behaviour of a component that uses it. As of version 5.49, you can do this sort of thing:

```js
import { mount, unmount } from 'svelte';
import { expect, test } from 'vitest';
import { setUserContext } from './context';
import MyComponent from './MyComponent.svelte';

test('MyComponent', () => {
function Wrapper(...args) {
setUserContext({ name: 'Bob' });
return MyComponent(...args);
}

const component = mount(Wrapper, {
target: document.body
});

expect(document.body.innerHTML).toBe('<h1>Hello Bob!</h1>');

unmount(component);
});
```

This approach also works with [`hydrate`](imperative-component-api#hydrate) and [`render`](imperative-component-api#render).

## Replacing global state

When you have state shared by many different components, you might be tempted to put it in its own module and just import it wherever it's needed:
Expand Down
12 changes: 4 additions & 8 deletions packages/svelte/src/internal/client/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,11 +208,9 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
pending: () => {}
},
(anchor_node) => {
if (context) {
push({});
var ctx = /** @type {ComponentContext} */ (component_context);
ctx.c = context;
}
push({});
var ctx = /** @type {ComponentContext} */ (component_context);
if (context) ctx.c = context;

if (events) {
// We can't spread the object or else we'd lose the state proxy stuff, if it is one
Expand Down Expand Up @@ -241,9 +239,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
}
}

if (context) {
pop();
}
pop();
}
);

Expand Down
12 changes: 4 additions & 8 deletions packages/svelte/src/internal/server/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,18 +611,14 @@ export class Renderer {

renderer.push(BLOCK_OPEN);

if (options.context) {
push();
/** @type {SSRContext} */ (ssr_context).c = options.context;
/** @type {SSRContext} */ (ssr_context).r = renderer;
}
push();
if (options.context) /** @type {SSRContext} */ (ssr_context).c = options.context;
/** @type {SSRContext} */ (ssr_context).r = renderer;

// @ts-expect-error
component(renderer, options.props ?? {});

if (options.context) {
pop();
}
pop();

renderer.push(BLOCK_CLOSE);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<script>
import { get } from './main.svelte';

const message = get();
</script>

<h1>{message}</h1>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { test } from '../../test';

export default test({
ssrHtml: `<div></div>`,
html: `<div><h1>hello</h1></div>`,

test() {}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script module>
import { createContext, mount } from 'svelte';
import Child from './Child.svelte';

/** @type {ReturnType<typeof createContext<string>>} */
const [get, set] = createContext();

export { get };

function Wrapper(Component) {
return (...args) => {
set('hello');
return Component(...args);
};
}
</script>

<div {@attach (target) => {
mount(Wrapper(Child), { target });
}}></div>
Loading