feat: allow use of createContext when instantiating components programmatically (#17575)

* feat: allow use of createContext when instantiating components programmatically

* docs
pull/17628/head
Rich Harris 3 days ago committed by GitHub
parent f05f946fea
commit fcbd3e325a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': minor
---
feat: allow use of createContext when instantiating components programmatically

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

@ -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
@ -241,9 +239,7 @@ function _mount(Component, { target, anchor, props = {}, events, context, intro
}
}
if (context) {
pop();
}
pop();
}
);

@ -617,18 +617,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);

@ -0,0 +1,7 @@
<script>
import { get } from './main.svelte';
const message = get();
</script>
<h1>{message}</h1>

@ -0,0 +1,8 @@
import { test } from '../../test';
export default test({
ssrHtml: `<div></div>`,
html: `<div><h1>hello</h1></div>`,
test() {}
});

@ -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…
Cancel
Save