I'm looking through the React documentation and I've come across this code.
import PropTypes from 'prop-types';
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: PropTypes.string
};
Is there a specific name for what they are doing here, where they are creating an object of propTypes on the class?
Is this just a React thing or can this be done in any ES6 code?
Why can't we just set a variable of propTypes within the class itself, why does it have to come outside the class?
That's because propTypes is a static1 property of the class, not associated with a certain instance of the class but the class itself. React's prop type-checking looks for propTypes of a certain component as a static property of the component's class. It's not on the class's prototype, but on the class itself.
If you were to declare it inside the class, it would be an instance property:
class Greeting extends React.Component {
get propTypes() { //Not static!
...
}
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Every Greeting component would then have the property propTypes and React won't properly type-check the props. You could use a static getter and declare it as a static property of the class though:
static get propTypes() {
...
}
The reason it's outside the class is based on preference. Static properties and getters can be used anywhere in ES2015 (or ES6) code.
In future versions of ECMAScript you can declare class properties. You can use class properties now with Babel and the transform-class-properties plugin (also in the stage-2 preset):
class Greeting extends React.Component {
static propTypes = {
...
}
}
This is syntactic sugar and works exactly the same as the static getter acting as a static property of the class.
1 Static meaning "unchanging" because the property doesn't change based on instance, it's the same across all instances because it's not associated with just one instnace.
PropTypes is a React specific API. It's a way that o tell React to perform basic runtime type-checking of the properties you pass to a component. In your example, you are saying that Greeting components can take a name property of type string, but it's not required.
The way that this special propTypes property is defined is not specific to React, though, it's just an ES6 static class property. It's a property associated with the class declaration itself (Greeting.propTypes), not instances of the class (new Greeting().propTypes is undefined). There's actually a newer ES7+ syntax you can use which is equivalent:
class Greeting extends React.Component {
static propTypes = {
name: PropTypes.string
};
render() { }
}
console.log(Greeting.propTypes) // { name: ... }
You declare / define a class and you instantiate objects of a certain class.
Related
I am new to React and was learning propTypes and the ways to implement them in React. One of the ways of propType implementation is via class fields, that is,
class Foo extends React.Component {
static PropTypes = {};
}
The question is why need to use static for propTypes and is it ok to omit static? I really hope for your beginner-friendly explanation since I have looked through the answer here react: why static propTypes but did not properly understand WHY?
Static props are those that belong to the class, not to an instance. This means that one class is shared throughout the entire application. This is known as the singleton pattern. The reason you would do this is because propTypes do not belong to a single instance, as mentioned in the question you linked. propTypes are used for type checking the props passed in, so there is no need for them to be tied to specific instances:
https://blog.logrocket.com/validating-react-component-props-with-prop-types-ef14b29963fc/
It is not OK to omit static, because the PropTypes library is expecting a class property when it looks for your prop definitions. If you omit static, you are defining an instance property, which is not how the library operates. Omitting the keyword will result in an instance property and your props will not be validated as expected.
If the static keyword is confusing, just think of it as doing the same exact thing as the following:
class Foo extends React.Component {
static propTypes = {}
}
// equivalent to
class Foo extends React.Component {}
Foo.propTypes = {}
This issue is only reproducable in babel (using babel-runtime-6.26.0).
Having the following structure
class Foo extends React.Component {
// ...
}
class Bar extends React.Component {
render() {
return this.props.component();
}
}
// somewhere:
<Bar component={Foo} />
worked and works for me in general, but when switching to babel I get the error message:
TypeError: Cannot call a class as a function in (classCallCheck.js:7). I've read that this is spec compliant behavior, but it does work outside of the babel environment.
Btw, if I use functional stateless components it works, of course. But I can't guarantee that the consumer of my lib is providing such a component and not a class based one.
How can I approach this using babel?
Is there a different pattern to use to pass components via properties? I believe it's common practice to pass in classes/class constructors via props to components.
A component can be either a function or a class. And ES6 classes cannot be called without new, at least the ones that are spec-compliant (this includes native and Babel classes).
This is not a problem since components shouldn't be instantiated manually. Both function and class components will be properly handled by ReactDOM.render:
class Bar extends React.Component {
render() {
const Component = this.props.component;
return <Component/>;
}
}
Try using React.createElement(this.props.component, props) instead.
Here's a working example:
class Foo extends React.Component {
render() { return "Foo" }
}
class Bar extends React.Component {
render() {
return React.createElement(this.props.component, {})
}
}
ReactDOM.render(
<Bar component={Foo} />,
document.getElementById('root')
);
https://codepen.io/anon/pen/QQGQYZ
For example, in vanilla JS I could easily do something like this:
class BaseClass extends React.Component { ... }
class Foo extends BaseClass { ... }
ReactDOM.render(<Foo />, someEl)
and it just works, no problem.
However, I can't seem to do such a simple thing in TypeScript. I tried to do
class BaseClass<P,S> extends React.Component<P,S> { ... }
interface IFooProps {...}
interface IFooState {...}
class Foo extends BaseClass<IFooProps, IFooState> { ... }
ReactDOM.render(<Foo />, someEl)
but that fails with errors like
ERROR in ./src/app.tsx
(44,17): error TS2605: JSX element type 'Foo' is not a constructor function for JSX elements.
Property 'setState' is missing in type 'Foo'.
ERROR in ./src/app.tsx
(44,17): error TS2607: JSX element class does not support attributes because it does not have a 'props' property
ERROR in ./src/Foo.tsx
(30,49): error TS2339: Property 'props' does not exist on type 'Foo'.
My question is, how do we extend React.Component in order to make a base class where child classes of the base class can still specify the types of their props and state, so that I can do
class Foo extends BaseClass<IFooProps, IFooState> { ... }
where BaseClass extends from React.Component?
The following code is perfectly fine:
class BaseClass<P,S> extends React.Component<P,S> { ... }
and works fine e.g. I use it to add custom behavior to my React components in alm : https://github.com/alm-tools/alm/blob/5ff516f0212f75c7365a56104413d34dddcbf429/src/app/ui.tsx#L11
I'm encapsulating a Menu called JellMenu, and I want to expose CustomMenuItem like JellMenu.Item instead of import CustomMenuItem. I have no idea how to do.
I took look at react-native-viewpager source code, and I think this maybe helpful:
var ViewPager = React.createClass({
statics: {
DataSource: ViewPagerDataSource,
},
It will expose ViewPagerDataSource as ViewPager.DataSource, right? I don't know exactly. what's the meaning of the keyword statics in Es5, and what's the alternative in Es6? Thank you in advance.
It isn't a keyword, it's just a property name in an object initializer being passed to React.createClass. From the documentation, it's used to specify static (non instance-specific) parts of a React class:
The statics object allows you to define static methods that can be called on the component class.
Although statics only works for React.createClass, you can still write static methods in ES6 notation. If you are using ES7, then you can also write static properties.The statics object allows you to define static methods that can be called on the component class. For example:
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
Methods defined within this block are static, meaning that you can run them before any component instances are created, and the methods do not have access to the props or state of your components. If you want to check the value of props in a static method, have the caller pass in the props as an argument to the static method.
Also, You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Or outside the class like this:
class Component extends React.Component {
....
}
Component.propTypes = {...}
Component.someMethod = function(){....}
Does the statics object work with ES6 classes in React?
class SomeComponent extends React.Component {
render() {
// ...
}
}
React.statics = {
someMethod: function() {
//...
}
};
Something like the above gives me undefined method someMethod when I do SomeComponent.someMethod()
statics only works with React.createClass. Simply declare the method as a static class method:
class SomeComponent extends React.Component {
static someMethod() {
//...
}
render() {
// ...
}
}
Regarding
React.statics = { ... }
You are literally creating a statics property on the React object. That property does not magically extend your component.
Although statics only works for React.createClass, you can still write static methods in ES6 notation. If you are using ES7, then you can also write static properties.
You can write statics inside ES6+ classes this way:
class Component extends React.Component {
static propTypes = {
...
}
static someMethod(){
}
}
Or outside the class like this:
class Component extends React.Component {
....
}
Component.propTypes = {...}
Component.someMethod = function(){....}
If you want to write it like the former, then you have to set stage: 0 on Babel (since its experimental).
Statics can be accessed without having to instantiate a component. Normally they aren't that useful but there are a few special cases. For example into routing when you leave the current page with doing a ACTION PERFORM then through Statics methods you can hold/ASKED the user whether he really want to leave the page.
For example:
exampleComponent= React.createClass({
statics:{
willTransitionFrom: function(transition,component){
// check any state here or value Aasked the user.
}
}
});
It exposes willTransitionTo and willTransitionFrom lifecycle methods. both are particular is useful as a static as you can actually cancel a transition before instantiating a component.
Stop. Just declare a class without extending React.Component and it will works.
class YourStaticClass {
static YourMethod(){
}
}
export default YourStaticClass;
in the other classes,
You can simply import your class and trigger the function
import YourStaticClass from "whatever";
...
YourStaticClass.YourMethod();
...
statics works only for React components, check docs.