Skip to content

Instantly share code, notes, and snippets.

@jamesknelson
Last active January 9, 2016 17:53
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jamesknelson/1481521444e1529d59aa to your computer and use it in GitHub Desktop.
Save jamesknelson/1481521444e1529d59aa to your computer and use it in GitHub Desktop.
Use parent context instead of owner context in facebook/react (#2112)
var ReactInstanceMap = require("react/lib/ReactInstanceMap");
var ReactLifeCycle = require("react/lib/ReactLifeCycle");
var ReactNativeComponent = require("react/lib/ReactNativeComponent");
var ReactReconciler = require("react/lib/ReactReconciler");
var emptyObject = require("react/lib/emptyObject");
var invariant = require("react/lib/invariant");
var warning = require("react/lib/warning");
var ReactCompositeComponentMixin = require('react/lib/ReactCompositeComponent').Mixin;
var {mountComponent, updateComponent} = ReactCompositeComponentMixin;
var nextMountID = 1;
ReactCompositeComponentMixin.mountComponent = function(rootID, transaction, context) {
this._context = context;
this._mountOrder = nextMountID++;
this._rootNodeID = rootID;
var publicProps = this._processProps(this._currentElement.props);
var publicContext = this._processContext(context);
var Component = ReactNativeComponent.getComponentClassForElement(
this._currentElement
);
// Initialize the public class
var inst = new Component(publicProps, publicContext);
// These should be set up in the constructor, but as a convenience for
// simpler class abstractions, we set them up after the fact.
inst.props = publicProps;
inst.context = publicContext;
inst.refs = emptyObject;
this._instance = inst;
// Store a reference from the instance back to the internal representation
ReactInstanceMap.set(inst, this);
if ("production" !== process.env.NODE_ENV) {
// Since plain JS classes are defined without any special initialization
// logic, we can not catch common errors early. Therefore, we have to
// catch them here, at initialization time, instead.
("production" !== process.env.NODE_ENV ? warning(
!inst.getInitialState ||
inst.getInitialState.isReactClassApproved,
'getInitialState was defined on %s, a plain JavaScript class. ' +
'This is only supported for classes created using React.createClass. ' +
'Did you mean to define a state property instead?',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
!inst.propTypes,
'propTypes was defined as an instance property on %s. Use a static ' +
'property to define propTypes instead.',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
!inst.contextTypes,
'contextTypes was defined as an instance property on %s. Use a ' +
'static property to define contextTypes instead.',
this.getName() || 'a component'
) : null);
("production" !== process.env.NODE_ENV ? warning(
typeof inst.componentShouldUpdate !== 'function',
'%s has a method called ' +
'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' +
'The name is phrased as a question because the function is ' +
'expected to return a value.',
(this.getName() || 'A component')
) : null);
}
var initialState = inst.state;
if (initialState === undefined) {
inst.state = initialState = null;
}
("production" !== process.env.NODE_ENV ? invariant(
typeof initialState === 'object' && !Array.isArray(initialState),
'%s.state: must be set to an object or null',
this.getName() || 'ReactCompositeComponent'
) : invariant(typeof initialState === 'object' && !Array.isArray(initialState)));
this._pendingStateQueue = null;
this._pendingReplaceState = false;
this._pendingForceUpdate = false;
var renderedElement;
var previouslyMounting = ReactLifeCycle.currentlyMountingInstance;
ReactLifeCycle.currentlyMountingInstance = this;
try {
if (inst.componentWillMount) {
inst.componentWillMount();
// When mounting, calls to `setState` by `componentWillMount` will set
// `this._pendingStateQueue` without triggering a re-render.
if (this._pendingStateQueue) {
inst.state = this._processPendingState(inst.props, inst.context);
}
}
renderedElement = this._renderValidatedComponent();
} finally {
ReactLifeCycle.currentlyMountingInstance = previouslyMounting;
}
this._renderedComponent = this._instantiateReactComponent(
renderedElement,
this._currentElement.type // The wrapping type
);
var markup = ReactReconciler.mountComponent(
this._renderedComponent,
rootID,
transaction,
this._processChildContext(context)
);
if (inst.componentDidMount) {
transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
}
return markup;
};
ReactCompositeComponentMixin.updateComponent = function(
transaction,
prevParentElement,
nextParentElement,
prevUnmaskedContext,
nextUnmaskedContext
) {
var inst = this._instance;
var nextContext = inst.context;
var nextProps = inst.props;
// Distinguish between a props update versus a simple state update
if (prevParentElement !== nextParentElement) {
nextContext = this._processContext(nextUnmaskedContext);
nextProps = this._processProps(nextParentElement.props);
// An update here will schedule an update but immediately set
// _pendingStateQueue which will ensure that any state updates gets
// immediately reconciled instead of waiting for the next batch.
if (inst.componentWillReceiveProps) {
inst.componentWillReceiveProps(nextProps, nextContext);
}
}
var nextState = this._processPendingState(nextProps, nextContext);
var shouldUpdate =
this._pendingForceUpdate ||
!inst.shouldComponentUpdate ||
inst.shouldComponentUpdate(nextProps, nextState, nextContext);
if ("production" !== process.env.NODE_ENV) {
("production" !== process.env.NODE_ENV ? warning(
typeof shouldUpdate !== 'undefined',
'%s.shouldComponentUpdate(): Returned undefined instead of a ' +
'boolean value. Make sure to return true or false.',
this.getName() || 'ReactCompositeComponent'
) : null);
}
if (shouldUpdate) {
this._pendingForceUpdate = false;
// Will set `this.props`, `this.state` and `this.context`.
this._performComponentUpdate(
nextParentElement,
nextProps,
nextState,
nextContext,
transaction,
nextUnmaskedContext
);
} else {
// If it's determined that a component should not update, we still want
// to set props and state but we shortcut the rest of the update.
this._currentElement = nextParentElement;
this._context = nextUnmaskedContext;
inst.props = nextProps;
inst.state = nextState;
inst.context = nextContext;
}
};
console.warn('Applied react-parent-context-patch!');
@natew
Copy link

natew commented Mar 18, 2015

Got this working with 0.13.1 but getting this error now:

TypeError: Cannot read property '_currentElement' of null

in:

ReactCompositeComponentMixin.updateComponent
contextPatch.js:123

@natew
Copy link

natew commented Mar 18, 2015

Which the debugger shows me as actually being this line:

this._performComponentUpdate(nextParentElement, nextProps, nextState, nextContext, transaction, nextUnmaskedContext);

To note, this is only when updating an existing page. May have something to do with react-router, will be investigating...

@natew
Copy link

natew commented Mar 18, 2015

Nevermind this worked perfectly, found out it was due to react-router. Apologies for all the posts.

@gurdasnijor
Copy link

@natew: Did you have any more context on this issue? Currently facing the same with react-router 0.13 in a willTransitionTo hook

@MatthewHerbst
Copy link

@natews + @gurdasnijor: bump. Have you guys resolved this? I'm running React 0.13.2 and React-Router 0.13.2 and getting that same error, Uncaught TypeError: Cannot read property '_currentElement' of null

@syeshchenko
Copy link

Same here... facing the same problem. Anyone got a solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment