| null} */
+ let subs = new Set();
+ let ctx = {};
+ ctx[Context._id] = this;
+
+ this.getChildContext = () => ctx;
+
+ this.componentWillUnmount = () => {
+ subs = null;
+ };
+
+ this.shouldComponentUpdate = function (_props) {
+ // @ts-expect-error even
+ if (this.props.value !== _props.value) {
+ subs.forEach(c => {
+ c._force = true;
+ enqueueRender(c);
+ });
+ }
+ };
+
+ this.sub = c => {
+ subs.add(c);
+ let old = c.componentWillUnmount;
+ c.componentWillUnmount = () => {
+ if (subs) {
+ subs.delete(c);
}
+ if (old) old.call(c);
};
+ };
+ }
- this.sub = c => {
- subs.add(c);
- let old = c.componentWillUnmount;
- c.componentWillUnmount = () => {
- if (subs) {
- subs.delete(c);
- }
- if (old) old.call(c);
- };
- };
- }
+ return props.children;
+ }
- return props.children;
- }
+ Context._id = '__cC' + i++;
+ Context._defaultValue = defaultValue;
+
+ /** @type {import('./internal').FunctionComponent} */
+ Context.Consumer = (props, contextValue) => {
+ return props.children(contextValue);
};
- // Devtools needs access to the context object when it
- // encounters a Provider. This is necessary to support
- // setting `displayName` on the context object instead
- // of on the component itself. See:
- // https://reactjs.org/docs/context.html#contextdisplayname
+ // we could also get rid of _contextRef entirely
+ Context.Provider =
+ Context._contextRef =
+ Context.Consumer.contextType =
+ Context;
- return (context.Provider._contextRef = context.Consumer.contextType =
- context);
+ return Context;
}
diff --git a/src/index-5.d.ts b/src/index-5.d.ts
index 7311010d71..6c7431cb00 100644
--- a/src/index-5.d.ts
+++ b/src/index-5.d.ts
@@ -84,7 +84,7 @@ export type ComponentProps<
? P
: C extends keyof JSXInternal.IntrinsicElements
? JSXInternal.IntrinsicElements[C]
- : never;
+ : {};
export interface FunctionComponent {
(props: RenderableProps
, context?: any): VNode | null;
diff --git a/src/index.d.ts b/src/index.d.ts
index 279af93ab2..379bbebaaf 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -84,7 +84,7 @@ export type ComponentProps<
? P
: C extends keyof JSXInternal.IntrinsicElements
? JSXInternal.IntrinsicElements[C]
- : never;
+ : {};
export interface FunctionComponent
{
(props: RenderableProps
, context?: any): ComponentChildren;
@@ -388,11 +388,12 @@ export type ContextType> = C extends Context
? T
: never;
-export interface Context {
- Consumer: Consumer;
- Provider: Provider;
+export interface Context extends preact.Provider {
+ Consumer: preact.Consumer;
+ Provider: preact.Provider;
displayName?: string;
}
+
export interface PreactContext extends Context {}
export function createContext(defaultValue: T): Context;
diff --git a/test/browser/createContext.test.js b/test/browser/createContext.test.js
index f5af75db58..41850aad90 100644
--- a/test/browser/createContext.test.js
+++ b/test/browser/createContext.test.js
@@ -57,6 +57,40 @@ describe('createContext', () => {
expect(scratch.innerHTML).to.equal('');
});
+ it('should pass context to a consumer (non-provider)', () => {
+ const Ctx = createContext(null);
+ const CONTEXT = { a: 'a' };
+
+ let receivedContext;
+
+ class Inner extends Component {
+ render(props) {
+ return {props.a}
;
+ }
+ }
+
+ sinon.spy(Inner.prototype, 'render');
+
+ render(
+
+
+
+ {data => {
+ receivedContext = data;
+ return ;
+ }}
+
+
+ ,
+ scratch
+ );
+
+ // initial render does not invoke anything but render():
+ expect(Inner.prototype.render).to.have.been.calledWithMatch(CONTEXT);
+ expect(receivedContext).to.equal(CONTEXT);
+ expect(scratch.innerHTML).to.equal('');
+ });
+
// This optimization helps
// to prevent a Provider from rerendering the children, this means
// we only propagate to children.
@@ -152,7 +186,8 @@ describe('createContext', () => {
it('should preserve provider context between different providers', () => {
const { Provider: ThemeProvider, Consumer: ThemeConsumer } =
createContext(null);
- const { Provider: DataProvider, Consumer: DataConsumer } = createContext(null);
+ const { Provider: DataProvider, Consumer: DataConsumer } =
+ createContext(null);
const THEME_CONTEXT = { theme: 'black' };
const DATA_CONTEXT = { global: 'a' };
diff --git a/test/ts/custom-elements.tsx b/test/ts/custom-elements.tsx
index c606d6a7f8..f943b7c930 100644
--- a/test/ts/custom-elements.tsx
+++ b/test/ts/custom-elements.tsx
@@ -41,7 +41,7 @@ interface WhateveElAttributes extends createElement.JSX.HTMLAttributes {
}
// Ensure context still works
-const { Provider, Consumer } = createContext({ contextValue: '' });
+const Ctx = createContext({ contextValue: '' });
// Sample component that uses custom elements
@@ -50,7 +50,7 @@ class SimpleComponent extends Component {
render() {
// Render inside div to ensure standard JSX elements still work
return (
-
+
{
@@ -73,13 +73,30 @@ class SimpleComponent extends Component {
>
{/* Ensure context still works */}
-
+
{({ contextValue }) => contextValue.toLowerCase()}
-
+
-
+
);
}
}
const component = ;
+class SimpleComponentWithContextAsProvider extends Component {
+ componentProp = 'componentProp';
+ render() {
+ // Render inside div to ensure standard JSX elements still work
+ return (
+
+
+ {/* Ensure context still works */}
+
+ {({ contextValue }) => contextValue.toLowerCase()}
+
+
+
+ );
+ }
+}
+const component2 = ;