Classes are now pure classes without a legacy factory around them.
Since classes will become just any function that returns a valid instance,
let's drop isValidClass.
There's some hacks in here for auto-mocking frameworks (jest) that mock the
prototype of these classes. These hacks allow these classes to be mounted.
React.DOM is becoming helper factories to generate ReactElements. They're not
classes. It will be ok to call them directly as functions, but not to use them
where a class is expected.
This makes ReactDOM a simple helper for creating ReactElements with the string tag as the type. The actual class is internal and created by instantiateReactComponent. Configurable using injection.
There's not a separate class for each tag. There's just a generic ReactDOMComponent which could take any tag name.
Invididual tags can be wrapped. When wrapping happens you can return the same tag again. If the wrapper returns the same string, then we fall back to the generic component. This avoids recursion in a single level wrapper.
This moves all logic around legacy descriptors to ReactLegacyDescriptor. This
is responsible for the layer that knows that createClass exports a legacy
factory. When passed one of these classes, it unwraps it to be a real class.
If it is passed a non legacy factory, it is assumed to be a non-react component
that needs to be invoked as a plain function.
The semantic change is that a descriptor is now always returned if passed a
legacy factory. Even if that factory is a mock. A mock would previously return
undefined.
For mocks, I treat the factory as the authoritative function. I call it to extract
the instance or fill it with an empty component placeholder.
Additionally, I make the classes take props as the first argument to the
constructor. This is what the new class system will do.
We currently need to set up some internals by calling the internal construct
method. Instead of doing that automatically in the constructor, I now move that
to a second pass so that mocks can get the plain props.
This means that we can assert that a mock has been called once it's mounted
with it's final props. Instead of the descriptor factory being called.
Breaking changes
- key/ref are no longer accessible on props but they are accessible on the
descriptors. This means that parents/owners can access it but not the
component itself.
- Descriptor factories are now plain functions and you can't rely on the
prototype or constructors of descriptors to identify the component type.
Existing descriptor factories are now wrapped in a legacy factory. Currently it
does nothing but it will give us a hook to track callers to factories that are
not using JSX but just invoking the function directly. It also proxies static
methods/properties to the underlying class. The newer factories don't have this
feature.
ReactTextComponent has it's own little factory because it's props is not an
object. This is a detail and will go away once ReactTextComponent no longer
needs descriptors.
This copies the propType and contextType validation to a wrapper around the
descriptor factory. By doing the validation early, we make it easier to track
down bugs. It also prepares for static type checking which should be done at the
usage site.
This validation is not yet active and is just logged using monitorCodeUse. This
will allow us to clean up callsites which would fail this new type of
validation.
I chose to copy the validation of abstracting it out since this is just an
intermediate step to avoid spamming consoles. This makes more a much cleaner
diff review/history. The original validation in the instance will be deleted as
soon as we can turn on the warnings.
Additionally, getDefaultProps are moved to become a static function which is
only executed once. It should be moved to statics but we don't have a
convenient way to merge mixins in statics right now. Deferring to ES6 classes.
This is still a breaking change since you can return an object or array from
getDefaultProps, which later gets mutated and now the shared instance is
mutated. Mutating an object that is passed into you from props is highly
discouraged and likely to lead to subtle bugs anyway. So I'm not too worried.
The defaultProps will later be resolved in the descriptor factory. This will
enable a perf optimizations where we don't create an unnecessary object
allocation when you use default props. It also means that ReactChildren.map
has access to resolved properties which gives them consistent behavior whether
or not the default prop is specified.
This copies the propType and contextType validation to a wrapper around the
descriptor factory. By doing the validation early, we make it easier to track
down bugs. It also prepares for static type checking which should be done at the
usage site.
This validation is not yet active and is just logged using monitorCodeUse. This
will allow us to clean up callsites which would fail this new type of
validation.
I chose to copy the validation instead of abstracting it out to a common abstraction. This is just an
intermediate step to avoid spamming consoles. The original validation in the instance will be deleted as soon as we can turn on the warnings at the callsite. Copy+Delete makes this a more a much cleaner diff review/history.
Additionally, getDefaultProps are moved to become a static function which is
only executed once. It should be moved to statics but we don't have a
convenient way to merge mixins in statics right now. Deferring to ES6 classes.
This is still a breaking change since you can return an object or array from
getDefaultProps, which later gets mutated and now the shared instance is
mutated. Mutating an object that is passed into you from props is highly
discouraged and likely to lead to subtle bugs anyway. So I'm not too worried.
The defaultProps are now resolved in the descriptor factory. This will enable
a perf optimizations where we don't create an unnecessary object allocation
when you use default props. It also means that ReactChildren.map has access to
resolved properties which gives them consistent behavior whether or not the
default prop is specified.
This is a breaking change since it can affect how mapping over children and
transferPropsTo works together with defaultProps.
grep -rl 'Copyright 2013 Facebook' static_upstream | xargs perl -pi -w -e s/Copyright 2013 Facebook/Copyright 2013-2014 Facebook/g;'
Not going to check in a script to do this since it will just change every year.
Closes#1006
This creates a membrane around the React component prototype. It warns if you
try to access properties on the component before it's unmounted. Before it's
mounted, it should be considered a descriptor and not an actual instance.
The workaround, for unknown types, is to access the constructor using
component.type which has static methods on it.