diff --git a/example/test/context_test.dart b/example/test/context_test.dart new file mode 100644 index 00000000..2b7673f1 --- /dev/null +++ b/example/test/context_test.dart @@ -0,0 +1,18 @@ +import 'dart:html'; + +import 'package:react/react_dom.dart' as react_dom; +import 'package:react/react_client.dart'; + +import 'react_test_components.dart'; + +void main() { + setClientConfiguration(); + + react_dom.render(contextComponent({}, + contextConsumerComponent({}), + ), querySelector('#content')); + + react_dom.render(contextComponent({}, + contextConsumerComponent({}), + ), querySelector('#content')); +} diff --git a/example/test/context_test.html b/example/test/context_test.html new file mode 100644 index 00000000..216fde50 --- /dev/null +++ b/example/test/context_test.html @@ -0,0 +1,14 @@ + + + + + context_test + + +
+ + + + + + diff --git a/example/test/react_test_components.dart b/example/test/react_test_components.dart index d1efb9ff..2e7d6021 100644 --- a/example/test/react_test_components.dart +++ b/example/test/react_test_components.dart @@ -152,3 +152,41 @@ class _MainComponent extends react.Component { } var mainComponent = react.registerComponent(() => new _MainComponent()); + +class _ContextComponent extends react.Component { + int _renderCount = 0; + + @override + Map getChildContext() => { + 'foo': 'bar', + 'renderCount': _renderCount + }; + + @override + Iterable get childContextKeys => const ['foo', 'renderCount']; + + render() { + _renderCount++; + + return react.ul({}, + 'ContextComponent.getChildContext(): ', + getChildContext().toString(), + props['children'] + ); + } +} +var contextComponent = react.registerComponent(() => new _ContextComponent()); + + +class _ContextConsumerComponent extends react.Component { + @override + Iterable get contextKeys => const ['foo', 'renderCount']; + + render() { + return react.ul({}, + 'ContextConsumerComponent.context: ', + context.toString(), + ); + } +} +var contextConsumerComponent = react.registerComponent(() => new _ContextConsumerComponent()); diff --git a/js_src/dart_helpers.js b/js_src/dart_helpers.js index 45939220..2819cef8 100644 --- a/js_src/dart_helpers.js +++ b/js_src/dart_helpers.js @@ -5,10 +5,10 @@ function _getProperty(obj, key) { return obj[key]; } function _setProperty(obj, key, value) { return obj[key] = value; } -function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics) { - return { +function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics, jsConfig) { + var config = { getInitialState: function() { - this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, componentStatics); + this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, this.context, componentStatics); return {}; }, componentWillMount: function() { @@ -17,14 +17,14 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati componentDidMount: function() { dartInteropStatics.handleComponentDidMount(this.dartComponent); }, - componentWillReceiveProps: function(nextProps) { - dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal); + componentWillReceiveProps: function(nextProps, nextContext) { + dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal, nextContext); }, - shouldComponentUpdate: function(nextProps, nextState) { - return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent); + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent, nextContext); }, - componentWillUpdate: function(nextProps, nextState) { - dartInteropStatics.handleComponentWillUpdate(this.dartComponent); + componentWillUpdate: function(nextProps, nextState, nextContext) { + dartInteropStatics.handleComponentWillUpdate(this.dartComponent, nextContext); }, componentDidUpdate: function(prevProps, prevState) { dartInteropStatics.handleComponentDidUpdate(this.dartComponent, prevProps.internal); @@ -36,6 +36,34 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati return dartInteropStatics.handleRender(this.dartComponent); } }; + + // React limits the accessible context entries + // to the keys specified in childContextTypes/contextTypes. + + var childContextKeys = jsConfig && jsConfig.childContextKeys; + var contextKeys = jsConfig && jsConfig.contextKeys; + + if (childContextKeys && childContextKeys.length !== 0) { + config.childContextTypes = {}; + for (var i = 0; i < childContextKeys.length; i++) { + config.childContextTypes[childContextKeys[i]] = React.PropTypes.object; + } + + // Only declare this when `hasChildContext` is true to avoid unnecessarily + // creating interop context objects for components that won't use it. + config.getChildContext = function() { + return dartInteropStatics.handleGetChildContext(this.dartComponent); + }; + } + + if (contextKeys && contextKeys.length !== 0) { + config.contextTypes = {}; + for (var i = 0; i < contextKeys.length; i++) { + config.contextTypes[contextKeys[i]] = React.PropTypes.object; + } + } + + return config; } function _markChildValidated(child) { diff --git a/lib/react.dart b/lib/react.dart index e64b4808..5ceef3fb 100644 --- a/lib/react.dart +++ b/lib/react.dart @@ -10,6 +10,8 @@ import 'package:react/src/typedefs.dart'; /// Top-level ReactJS [Component class](https://facebook.github.io/react/docs/react-component.html) /// which provides the [ReactJS Component API](https://facebook.github.io/react/docs/react-component.html#reference) abstract class Component { + Map _context; + /// A private field that backs [props], which is exposed via getter/setter so /// it can be overridden in strong mode. /// @@ -37,6 +39,12 @@ abstract class Component { /// TODO: Switch back to a plain field once this issue is fixed. Ref _ref; + /// The React context map of this component, passed down from its ancestors' [getChildContext] value. + /// + /// Only keys declared in this component's [contextKeys] will be present. + Map get context => _context; + set context(Map value) => _context = value; + /// ReactJS [Component] props. /// /// Related: [state] @@ -81,13 +89,18 @@ abstract class Component { /// Bind the value of input to [state[key]]. bind(key) => [state[key], (value) => setState({key: value})]; - initComponentInternal(props, _jsRedraw, [Ref ref, _jsThis]) { + initComponentInternal(props, _jsRedraw, [Ref ref, _jsThis, context]) { this._jsRedraw = _jsRedraw; this.ref = ref; this._jsThis = _jsThis; + _initContext(context); _initProps(props); } + _initContext(context) { + _context = new Map.from(context ?? {}); + } + _initProps(props) { this.props = new Map.from(props); this.nextProps = this.props; @@ -243,6 +256,21 @@ abstract class Component { /// See: void componentWillUnmount() {} + /// Returns a Map of context to be passed to descendant components. + /// + /// Only keys present in [childContextKeys] will be used; all others will be ignored. + Map getChildContext() => const {}; + + /// The keys this component uses in its child context map (returned by [getChildContext]). + /// + /// __This method is called only once, upon component registration.__ + Iterable get childContextKeys => const []; + + /// The keys of context used by this component. + /// + /// __This method is called only once, upon component registration.__ + Iterable get contextKeys => const []; + /// Invoked once before the `Component` is mounted. The return value will be used as the initial value of [state]. /// /// See: diff --git a/lib/react.js b/lib/react.js index 855306e4..3f26bc1a 100644 --- a/lib/react.js +++ b/lib/react.js @@ -4324,10 +4324,10 @@ module.exports = ReactPropTypesSecret; function _getProperty(obj, key) { return obj[key]; } function _setProperty(obj, key, value) { return obj[key] = value; } -function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics) { - return { +function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics, jsConfig) { + var config = { getInitialState: function() { - this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, componentStatics); + this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, this.context, componentStatics); return {}; }, componentWillMount: function() { @@ -4336,14 +4336,14 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati componentDidMount: function() { dartInteropStatics.handleComponentDidMount(this.dartComponent); }, - componentWillReceiveProps: function(nextProps) { - dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal); + componentWillReceiveProps: function(nextProps, nextContext) { + dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal, nextContext); }, - shouldComponentUpdate: function(nextProps, nextState) { - return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent); + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent, nextContext); }, - componentWillUpdate: function(nextProps, nextState) { - dartInteropStatics.handleComponentWillUpdate(this.dartComponent); + componentWillUpdate: function(nextProps, nextState, nextContext) { + dartInteropStatics.handleComponentWillUpdate(this.dartComponent, nextContext); }, componentDidUpdate: function(prevProps, prevState) { dartInteropStatics.handleComponentDidUpdate(this.dartComponent, prevProps.internal); @@ -4355,6 +4355,34 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati return dartInteropStatics.handleRender(this.dartComponent); } }; + + // React limits the accessible context entries + // to the keys specified in childContextTypes/contextTypes. + + var childContextKeys = jsConfig && jsConfig.childContextKeys; + var contextKeys = jsConfig && jsConfig.contextKeys; + + if (childContextKeys && childContextKeys.length !== 0) { + config.childContextTypes = {}; + for (var i = 0; i < childContextKeys.length; i++) { + config.childContextTypes[childContextKeys[i]] = React.PropTypes.object; + } + + // Only declare this when `hasChildContext` is true to avoid unnecessarily + // creating interop context objects for components that won't use it. + config.getChildContext = function() { + return dartInteropStatics.handleGetChildContext(this.dartComponent); + }; + } + + if (contextKeys && contextKeys.length !== 0) { + config.contextTypes = {}; + for (var i = 0; i < contextKeys.length; i++) { + config.contextTypes[contextKeys[i]] = React.PropTypes.object; + } + } + + return config; } function _markChildValidated(child) { diff --git a/lib/react_client.dart b/lib/react_client.dart index 30c0e561..ebe170db 100644 --- a/lib/react_client.dart +++ b/lib/react_client.dart @@ -178,12 +178,32 @@ dynamic _convertArgsToChildren(List childrenArgs) { } } +@JS('Object.keys') +external List _objectKeys(Object object); + +InteropContextValue _jsifyContext(Map context) { + var interopContext = new InteropContextValue(); + context.forEach((key, value) { + setProperty(interopContext, key, new ReactDartContextInternal(value)); + }); + + return interopContext; +} + +Map _unjsifyContext(InteropContextValue interopContext) { + // TODO consider using `contextKeys` for this if perf of objectKeys is bad. + return new Map.fromIterable(_objectKeys(interopContext), value: (key) { + ReactDartContextInternal internal = getProperty(interopContext, key); + return internal.value; + }); +} + /// The static methods that proxy JS component lifecycle methods to Dart components. final ReactDartInteropStatics _dartInteropStatics = (() { var zone = Zone.current; /// Wrapper for [Component.getInitialState]. - Component initComponent(ReactComponent jsThis, ReactDartComponentInternal internal, ComponentStatics componentStatics) => zone.run(() { + Component initComponent(ReactComponent jsThis, ReactDartComponentInternal internal, InteropContextValue context, ComponentStatics componentStatics) => zone.run(() { void jsRedraw() { jsThis.setState(emptyJsMap); } @@ -197,7 +217,7 @@ final ReactDartInteropStatics _dartInteropStatics = (() { }; Component component = componentStatics.componentFactory() - ..initComponentInternal(internal.props, jsRedraw, getRef, jsThis) + ..initComponentInternal(internal.props, jsRedraw, getRef, jsThis, _unjsifyContext(context)) ..initStateInternal(); // Return the component so that the JS proxying component can store it, @@ -205,6 +225,10 @@ final ReactDartInteropStatics _dartInteropStatics = (() { return component; }); + InteropContextValue handleGetChildContext(Component component) => zone.run(() { + return _jsifyContext(component.getChildContext()); + }); + /// Wrapper for [Component.componentWillMount]. void handleComponentWillMount(Component component) => zone.run(() { component @@ -225,9 +249,12 @@ final ReactDartInteropStatics _dartInteropStatics = (() { /// 1. Update [Component.props] using the value stored to [Component.nextProps] /// in `componentWillReceiveProps`. /// 2. Update [Component.state] by calling [Component.transferComponentState] - void _afterPropsChange(Component component) { + /// 3. Update [Component.context] with the latest + void _afterPropsChange(Component component, InteropContextValue nextContext) { component.props = component.nextProps; // [1] component.transferComponentState(); // [2] + // [3] + component.context = _unjsifyContext(nextContext); } void _clearPrevState(Component component) { @@ -248,7 +275,7 @@ final ReactDartInteropStatics _dartInteropStatics = (() { } /// Wrapper for [Component.componentWillReceiveProps]. - void handleComponentWillReceiveProps(Component component, ReactDartComponentInternal nextInternal) => zone.run(() { + void handleComponentWillReceiveProps(Component component, ReactDartComponentInternal nextInternal, InteropContextValue nextContext) => zone.run(() { var nextProps = _getNextProps(component, nextInternal); component ..nextProps = nextProps @@ -256,14 +283,14 @@ final ReactDartInteropStatics _dartInteropStatics = (() { }); /// Wrapper for [Component.shouldComponentUpdate]. - bool handleShouldComponentUpdate(Component component) => zone.run(() { + bool handleShouldComponentUpdate(Component component, InteropContextValue nextContext) => zone.run(() { _callSetStateTransactionalCallbacks(component); if (component.shouldComponentUpdate(component.nextProps, component.nextState)) { return true; } else { // If component should not update, update props / transfer state because componentWillUpdate will not be called. - _afterPropsChange(component); + _afterPropsChange(component, nextContext); _callSetStateCallbacks(component); // Clear out prevState after it's done being used so it's not retained _clearPrevState(component); @@ -272,9 +299,9 @@ final ReactDartInteropStatics _dartInteropStatics = (() { }); /// Wrapper for [Component.componentWillUpdate]. - void handleComponentWillUpdate(Component component) => zone.run(() { + void handleComponentWillUpdate(Component component, InteropContextValue nextContext) => zone.run(() { component.componentWillUpdate(component.nextProps, component.nextState); - _afterPropsChange(component); + _afterPropsChange(component, nextContext); }); /// Wrapper for [Component.componentDidUpdate]. @@ -304,6 +331,7 @@ final ReactDartInteropStatics _dartInteropStatics = (() { return new ReactDartInteropStatics( initComponent: allowInterop(initComponent), + handleGetChildContext: allowInterop(handleGetChildContext), handleComponentWillMount: allowInterop(handleComponentWillMount), handleComponentDidMount: allowInterop(handleComponentDidMount), handleComponentWillReceiveProps: allowInterop(handleComponentWillReceiveProps), @@ -318,18 +346,24 @@ final ReactDartInteropStatics _dartInteropStatics = (() { /// Returns a new [ReactComponentFactory] which produces a new JS /// [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass). ReactDartComponentFactoryProxy _registerComponent(ComponentFactory componentFactory, [Iterable skipMethods = const []]) { + var componentInstance = componentFactory(); var componentStatics = new ComponentStatics(componentFactory); + var jsConfig = new JsComponentConfig( + childContextKeys: componentInstance.childContextKeys, + contextKeys: componentInstance.contextKeys, + ); + /// Create the JS [`ReactClass` component class](https://facebook.github.io/react/docs/top-level-api.html#react.createclass) /// with custom JS lifecycle methods. var reactComponentClass = React.createClass( - createReactDartComponentClassConfig(_dartInteropStatics, componentStatics) - ..displayName = componentFactory().displayName + createReactDartComponentClassConfig(_dartInteropStatics, componentStatics, jsConfig) + ..displayName = componentInstance.displayName ); // Cache default props and store them on the ReactClass so they can be used // by ReactDartComponentFactoryProxy and externally. - final Map defaultProps = new Map.unmodifiable(componentFactory().getDefaultProps()); + final Map defaultProps = new Map.unmodifiable(componentInstance.getDefaultProps()); reactComponentClass.dartDefaultProps = defaultProps; return new ReactDartComponentFactoryProxy(reactComponentClass); diff --git a/lib/react_client/react_interop.dart b/lib/react_client/react_interop.dart index b26188ab..10296df5 100644 --- a/lib/react_client/react_interop.dart +++ b/lib/react_client/react_interop.dart @@ -83,6 +83,8 @@ class ReactClassConfig { Function componentWillUpdate, Function componentDidUpdate, Function componentWillUnmount, + Function getChildContext, + Map childContextTypes, Function getDefaultProps, Function getInitialState, Function render @@ -158,6 +160,18 @@ class ReactComponent { // Interop internals // ---------------------------------------------------------------------------- +/// A JavaScript interop class representing a value in a React JS `context` object. +/// +/// Used for storing/accessing Dart [ReactDartContextInternal] objects in `context` +/// in a way that's opaque to the JS, and avoids the need to use dart2js interceptors. +/// +/// __For internal/advanced use only.__ +@JS() +@anonymous +class InteropContextValue { + external factory InteropContextValue(); +} + /// A JavaScript interop class representing a React JS `props` object. /// /// Used for storing/accessing [ReactDartComponentInternal] objects in @@ -178,8 +192,7 @@ class InteropProps { external factory InteropProps({ReactDartComponentInternal internal, String key, dynamic ref}); } -/// Internal react-dart information used to proxy React JS lifecycle to Dart -/// [Component] instances. +/// A Dart object that stores . /// /// __For internal/advanced use only.__ class ReactDartComponentInternal { @@ -190,6 +203,16 @@ class ReactDartComponentInternal { Map props; } +/// Internal react-dart information used to proxy React JS lifecycle to Dart +/// [Component] instances. +/// +/// __For internal/advanced use only.__ +class ReactDartContextInternal { + final dynamic value; + + ReactDartContextInternal(this.value); +} + /// Marks [child] as validated, as if it were passed into [React.createElement] /// as a variadic child. /// @@ -213,14 +236,20 @@ void markChildrenValidated(List children) { /// [dartInteropStatics] and [componentStatics] internally to proxy between /// the JS and Dart component instances. @JS('_createReactDartComponentClassConfig') -external ReactClassConfig createReactDartComponentClassConfig(ReactDartInteropStatics dartInteropStatics, ComponentStatics componentStatics); - -typedef Component _InitComponent(ReactComponent jsThis, ReactDartComponentInternal internal, ComponentStatics componentStatics); +external ReactClassConfig createReactDartComponentClassConfig( + ReactDartInteropStatics dartInteropStatics, + ComponentStatics componentStatics, + [JsComponentConfig jsConfig] +); + +typedef Component _InitComponent(ReactComponent jsThis, ReactDartComponentInternal internal, InteropContextValue context, ComponentStatics componentStatics); +typedef InteropContextValue _HandleGetChildContext(Component component); typedef void _HandleComponentWillMount(Component component); typedef void _HandleComponentDidMount(Component component); -typedef void _HandleComponentWillReceiveProps(Component component, ReactDartComponentInternal nextInternal); -typedef bool _HandleShouldComponentUpdate(Component component); -typedef void _HandleComponentWillUpdate(Component component); +typedef void _HandleComponentWillReceiveProps(Component component, ReactDartComponentInternal nextInternal, InteropContextValue nextContext); +typedef bool _HandleShouldComponentUpdate(Component component, InteropContextValue nextContext); +typedef void _HandleComponentWillUpdate(Component component, InteropContextValue nextContext); +// Ignore prevContext in componentDidUpdate, since it's not supported in React 16 typedef void _HandleComponentDidUpdate(Component component, ReactDartComponentInternal prevInternal); typedef void _HandleComponentWillUnmount(Component component); typedef dynamic _HandleRender(Component component); @@ -231,6 +260,7 @@ typedef dynamic _HandleRender(Component component); class ReactDartInteropStatics { external factory ReactDartInteropStatics({ _InitComponent initComponent, + _HandleGetChildContext handleGetChildContext, _HandleComponentWillMount handleComponentWillMount, _HandleComponentDidMount handleComponentDidMount, _HandleComponentWillReceiveProps handleComponentWillReceiveProps, @@ -253,3 +283,14 @@ class ComponentStatics { ComponentStatics(this.componentFactory); } + +/// Additional configuration passed to [createReactDartComponentClassConfig] +/// that needs to be directly accessible by that JS code. +@JS() +@anonymous +class JsComponentConfig { + external factory JsComponentConfig({ + Iterable childContextKeys, + Iterable contextKeys, + }); +} diff --git a/lib/react_prod.js b/lib/react_prod.js index 2ce98061..718358c7 100644 --- a/lib/react_prod.js +++ b/lib/react_prod.js @@ -16,10 +16,10 @@ function _getProperty(obj, key) { return obj[key]; } function _setProperty(obj, key, value) { return obj[key] = value; } -function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics) { - return { +function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics, jsConfig) { + var config = { getInitialState: function() { - this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, componentStatics); + this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, this.context, componentStatics); return {}; }, componentWillMount: function() { @@ -28,14 +28,14 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati componentDidMount: function() { dartInteropStatics.handleComponentDidMount(this.dartComponent); }, - componentWillReceiveProps: function(nextProps) { - dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal); + componentWillReceiveProps: function(nextProps, nextContext) { + dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal, nextContext); }, - shouldComponentUpdate: function(nextProps, nextState) { - return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent); + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent, nextContext); }, - componentWillUpdate: function(nextProps, nextState) { - dartInteropStatics.handleComponentWillUpdate(this.dartComponent); + componentWillUpdate: function(nextProps, nextState, nextContext) { + dartInteropStatics.handleComponentWillUpdate(this.dartComponent, nextContext); }, componentDidUpdate: function(prevProps, prevState) { dartInteropStatics.handleComponentDidUpdate(this.dartComponent, prevProps.internal); @@ -47,6 +47,34 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati return dartInteropStatics.handleRender(this.dartComponent); } }; + + // React limits the accessible context entries + // to the keys specified in childContextTypes/contextTypes. + + var childContextKeys = jsConfig && jsConfig.childContextKeys; + var contextKeys = jsConfig && jsConfig.contextKeys; + + if (childContextKeys && childContextKeys.length !== 0) { + config.childContextTypes = {}; + for (var i = 0; i < childContextKeys.length; i++) { + config.childContextTypes[childContextKeys[i]] = React.PropTypes.object; + } + + // Only declare this when `hasChildContext` is true to avoid unnecessarily + // creating interop context objects for components that won't use it. + config.getChildContext = function() { + return dartInteropStatics.handleGetChildContext(this.dartComponent); + }; + } + + if (contextKeys && contextKeys.length !== 0) { + config.contextTypes = {}; + for (var i = 0; i < contextKeys.length; i++) { + config.contextTypes[contextKeys[i]] = React.PropTypes.object; + } + } + + return config; } function _markChildValidated(child) { diff --git a/lib/react_with_addons.js b/lib/react_with_addons.js index ef122fab..46e5cc6f 100644 --- a/lib/react_with_addons.js +++ b/lib/react_with_addons.js @@ -5965,10 +5965,10 @@ module.exports = ReactPropTypesSecret; function _getProperty(obj, key) { return obj[key]; } function _setProperty(obj, key, value) { return obj[key] = value; } -function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics) { - return { +function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics, jsConfig) { + var config = { getInitialState: function() { - this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, componentStatics); + this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, this.context, componentStatics); return {}; }, componentWillMount: function() { @@ -5977,14 +5977,14 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati componentDidMount: function() { dartInteropStatics.handleComponentDidMount(this.dartComponent); }, - componentWillReceiveProps: function(nextProps) { - dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal); + componentWillReceiveProps: function(nextProps, nextContext) { + dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal, nextContext); }, - shouldComponentUpdate: function(nextProps, nextState) { - return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent); + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent, nextContext); }, - componentWillUpdate: function(nextProps, nextState) { - dartInteropStatics.handleComponentWillUpdate(this.dartComponent); + componentWillUpdate: function(nextProps, nextState, nextContext) { + dartInteropStatics.handleComponentWillUpdate(this.dartComponent, nextContext); }, componentDidUpdate: function(prevProps, prevState) { dartInteropStatics.handleComponentDidUpdate(this.dartComponent, prevProps.internal); @@ -5996,6 +5996,34 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati return dartInteropStatics.handleRender(this.dartComponent); } }; + + // React limits the accessible context entries + // to the keys specified in childContextTypes/contextTypes. + + var childContextKeys = jsConfig && jsConfig.childContextKeys; + var contextKeys = jsConfig && jsConfig.contextKeys; + + if (childContextKeys && childContextKeys.length !== 0) { + config.childContextTypes = {}; + for (var i = 0; i < childContextKeys.length; i++) { + config.childContextTypes[childContextKeys[i]] = React.PropTypes.object; + } + + // Only declare this when `hasChildContext` is true to avoid unnecessarily + // creating interop context objects for components that won't use it. + config.getChildContext = function() { + return dartInteropStatics.handleGetChildContext(this.dartComponent); + }; + } + + if (contextKeys && contextKeys.length !== 0) { + config.contextTypes = {}; + for (var i = 0; i < contextKeys.length; i++) { + config.contextTypes[contextKeys[i]] = React.PropTypes.object; + } + } + + return config; } function _markChildValidated(child) { diff --git a/lib/react_with_react_dom_prod.js b/lib/react_with_react_dom_prod.js index 82e9ddd3..b2fbb6bc 100644 --- a/lib/react_with_react_dom_prod.js +++ b/lib/react_with_react_dom_prod.js @@ -16,10 +16,10 @@ function _getProperty(obj, key) { return obj[key]; } function _setProperty(obj, key, value) { return obj[key] = value; } -function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics) { - return { +function _createReactDartComponentClassConfig(dartInteropStatics, componentStatics, jsConfig) { + var config = { getInitialState: function() { - this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, componentStatics); + this.dartComponent = dartInteropStatics.initComponent(this, this.props.internal, this.context, componentStatics); return {}; }, componentWillMount: function() { @@ -28,14 +28,14 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati componentDidMount: function() { dartInteropStatics.handleComponentDidMount(this.dartComponent); }, - componentWillReceiveProps: function(nextProps) { - dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal); + componentWillReceiveProps: function(nextProps, nextContext) { + dartInteropStatics.handleComponentWillReceiveProps(this.dartComponent, nextProps.internal, nextContext); }, - shouldComponentUpdate: function(nextProps, nextState) { - return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent); + shouldComponentUpdate: function(nextProps, nextState, nextContext) { + return dartInteropStatics.handleShouldComponentUpdate(this.dartComponent, nextContext); }, - componentWillUpdate: function(nextProps, nextState) { - dartInteropStatics.handleComponentWillUpdate(this.dartComponent); + componentWillUpdate: function(nextProps, nextState, nextContext) { + dartInteropStatics.handleComponentWillUpdate(this.dartComponent, nextContext); }, componentDidUpdate: function(prevProps, prevState) { dartInteropStatics.handleComponentDidUpdate(this.dartComponent, prevProps.internal); @@ -47,6 +47,34 @@ function _createReactDartComponentClassConfig(dartInteropStatics, componentStati return dartInteropStatics.handleRender(this.dartComponent); } }; + + // React limits the accessible context entries + // to the keys specified in childContextTypes/contextTypes. + + var childContextKeys = jsConfig && jsConfig.childContextKeys; + var contextKeys = jsConfig && jsConfig.contextKeys; + + if (childContextKeys && childContextKeys.length !== 0) { + config.childContextTypes = {}; + for (var i = 0; i < childContextKeys.length; i++) { + config.childContextTypes[childContextKeys[i]] = React.PropTypes.object; + } + + // Only declare this when `hasChildContext` is true to avoid unnecessarily + // creating interop context objects for components that won't use it. + config.getChildContext = function() { + return dartInteropStatics.handleGetChildContext(this.dartComponent); + }; + } + + if (contextKeys && contextKeys.length !== 0) { + config.contextTypes = {}; + for (var i = 0; i < contextKeys.length; i++) { + config.contextTypes[contextKeys[i]] = React.PropTypes.object; + } + } + + return config; } function _markChildValidated(child) {