Skip to content

Commit fcbd3e3

Browse files
authored
feat: allow use of createContext when instantiating components programmatically (sveltejs#17575)
* feat: allow use of createContext when instantiating components programmatically * docs
1 parent f05f946 commit fcbd3e3

File tree

7 files changed

+74
-16
lines changed

7 files changed

+74
-16
lines changed

.changeset/orange-ants-greet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': minor
3+
---
4+
5+
feat: allow use of createContext when instantiating components programmatically

documentation/docs/06-runtime/02-context.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,32 @@ import { createContext } from 'svelte';
9797
export const [getUserContext, setUserContext] = createContext<User>();
9898
```
9999

100+
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:
101+
102+
```js
103+
import { mount, unmount } from 'svelte';
104+
import { expect, test } from 'vitest';
105+
import { setUserContext } from './context';
106+
import MyComponent from './MyComponent.svelte';
107+
108+
test('MyComponent', () => {
109+
function Wrapper(...args) {
110+
setUserContext({ name: 'Bob' });
111+
return MyComponent(...args);
112+
}
113+
114+
const component = mount(Wrapper, {
115+
target: document.body
116+
});
117+
118+
expect(document.body.innerHTML).toBe('<h1>Hello Bob!</h1>');
119+
120+
unmount(component);
121+
});
122+
```
123+
124+
This approach also works with [`hydrate`](imperative-component-api#hydrate) and [`render`](imperative-component-api#render).
125+
100126
## Replacing global state
101127

102128
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:

packages/svelte/src/internal/client/render.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,9 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
208208
pending: () => {}
209209
},
210210
(anchor_node) => {
211-
if (context) {
212-
push({});
213-
var ctx = /** @type {ComponentContext} */ (component_context);
214-
ctx.c = context;
215-
}
211+
push({});
212+
var ctx = /** @type {ComponentContext} */ (component_context);
213+
if (context) ctx.c = context;
216214

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

244-
if (context) {
245-
pop();
246-
}
242+
pop();
247243
}
248244
);
249245

packages/svelte/src/internal/server/renderer.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -617,18 +617,14 @@ export class Renderer {
617617

618618
renderer.push(BLOCK_OPEN);
619619

620-
if (options.context) {
621-
push();
622-
/** @type {SSRContext} */ (ssr_context).c = options.context;
623-
/** @type {SSRContext} */ (ssr_context).r = renderer;
624-
}
620+
push();
621+
if (options.context) /** @type {SSRContext} */ (ssr_context).c = options.context;
622+
/** @type {SSRContext} */ (ssr_context).r = renderer;
625623

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

629-
if (options.context) {
630-
pop();
631-
}
627+
pop();
632628

633629
renderer.push(BLOCK_CLOSE);
634630

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<script>
2+
import { get } from './main.svelte';
3+
4+
const message = get();
5+
</script>
6+
7+
<h1>{message}</h1>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { test } from '../../test';
2+
3+
export default test({
4+
ssrHtml: `<div></div>`,
5+
html: `<div><h1>hello</h1></div>`,
6+
7+
test() {}
8+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script module>
2+
import { createContext, mount } from 'svelte';
3+
import Child from './Child.svelte';
4+
5+
/** @type {ReturnType<typeof createContext<string>>} */
6+
const [get, set] = createContext();
7+
8+
export { get };
9+
10+
function Wrapper(Component) {
11+
return (...args) => {
12+
set('hello');
13+
return Component(...args);
14+
};
15+
}
16+
</script>
17+
18+
<div {@attach (target) => {
19+
mount(Wrapper(Child), { target });
20+
}}></div>

0 commit comments

Comments
 (0)