I came across this code snippet on Codepen:
const { Component, createElement, PropTypes } = React;
const source = `<p>Hello, my name is {{name}}. I work for {{employer}}. I have {{kids.length}} kids:</p> <ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>`;
const template = Handlebars.compile( source );
class StarshipEnterprise extends Component {
static propTypes = {
name: PropTypes.string,
employer: PropTypes.string,
kids: PropTypes.arrayOf( PropTypes.object ),
};
static defaultProps = {
name: "Data",
employer: "United Federation of Planets",
kids: [
{
name: "Lal",
age: "2"
},
]
};
render () {
return <div className="container" dangerouslySetInnerHTML={{ __html: template( this.props ) }} />;
}
}
ReactDOM.render( createElement( StarshipEnterprise ), document.getElementById( "app" ) );
Within the StarshipEnterprise class, they are using the word static in front of the object names. I've tried googling what these are and what they are doing, but all I'm getting is "The static keyword defines a static method for a class."
As a beginner, I have no idea what this means. Can anyone point me in the right direction on what these are doing or why I would need to use them?
Static means a property that belongs to class only but not for it's instances.
class Triple {
let triplevar = 0;
static tripleFunc(n) {
if(n == 1) { return 1;}
else { return n*3; }
}
}
Now we can use above static method through the class name:
Triple.tripleFunc(3); //Valid
But not by creating it's instance:
let tp = new Triple();
tp.tripleFunc(6); //Not-Valid
Earlier in React we used to define propTypes and defaultProps outside the class by using following syntax :
import React, {Component} from 'react';
class SomeClass extends Component {
constructor(props){
super(props)
}
}
SomeClass.proptypes = {};
SomeClass.defaultProps = {};
Now we are defining it inside the class itself using static keyword here.
class SomeClass extends Component {
constructor(props){
super(props)
}
static propTypes = {};
static defaultProps = {};
}
When we are importing SomeClass to another component then propTypes and defaultProps will be available in that component and can be accessed by using directly as:
class ChildClass extends SomeClass {
constructor(props) {
super(props);
this.instanceSomeClass = new SomeClass();
console.log(this.instanceSomeClass.propTypes); // Will get undefined here
console.log(SomeClass.propTypes) // it will be fine
}
}
But we should not use it like this as when we are generating build it may remove, and we will be getting warning for the same.
The static keyword allows react to get the values of propTypes and defaultProps, without initializing your component.
Refer to the MDN documentation
The static keyword defines a static method for a class. Static methods aren't called on instances of the class. Instead, they're called on the class itself. These are often utility functions, such as functions to create or clone objects.M
Related
Background
I created a fully functional component. The component has state and props, and there are many methods inside. My component should work differently according to the os(ios / android). So I solved this problem by if statement like below.
if( platform.os == 'ios') { ... } else { ... }
The problem was that as the code volume increased, there was a problem with readability, and I decided to make a separate component for IOS and for Android. The first thing that came to mind was inheritance because ES6 and Typescript Support Class. The picture of concept is this.
However, React does not recommend inheritance. So I was just going to hand over the functions overridden by props to the Speech component in the SpeechIOS component's render function.
The code is as follows.
Speech.tsx
type Props = {
team: number,
onSpeechResults: (result: string) => void
...
}
type States = {
active: boolean;
error: string;
result: string;
...
};
export default class Speech extends Component<Props,States> {
state = { ... };
constructor(props: Props) {
super(props);
...
}
// render
render() {
...
return (
<ImageBackground source={require("../images/default-background.jpeg")} style={styles.full}>
...
</ImageBackground>
);
}
sendCommand = (code: number, speed: number, callback?: () => void) => { ... }
getMatchedSpell = (spellWord: string): { code: number, speed: number } => { ... }
onSpeechResults(e: Voice.Results) { ... };
...
}
SpeechIOS.tsx
import Speech from './Speech';
type Props = {}
type States = {}
export default class SpeechIOS extends Component<Props,States> {
constructor(props: Props) {
super(props);
...
}
// render
render() {
...
return ( <Speech team="1" onSpeechResults={this.onSpeechResults}></Speech> );
}
sayHello() {
console.log("Hello!!");
}
// I want that Speech Component call this onSpeechResults function
onSpeechResults(result: string) {
this.setState({...});
let temp = this.getMatchedSpell( ... ); // which is in Speech Component
this.sendCommand( 10, 100 ... ); // which is in Speech Component
this.sayHello(); // which is in SpeechIOS component only
... other things..
};
}
Problem.
As you can see, the onSpeechResults which is in SpeechIOS Component use some functions in Speech Component and in SpeechIOS Component also.
So, How to solve this problem? Should I use Inheritance?
Alternatively, break out any common logic into a utility function like SpeechUtil.ts, a whole new file that holds this shared logic and each util function, and exports them. Then, each component separately imports them. That ensures that if you ever update that logic it affects both components
You should define a top level component that defines the shared props and methods, and use those to render either your ios or android component. pass the props and methods to the children. This is composition which is favored over inheritance in react. example:
class Speech extends React.Component {
state ={shared: 'state'}
sharedMethod = () => {
this.setState({blah: 'blah})
}
render() {
const Root = Platform.select({
ios: SpeechIOS,
android: SpeechAndroid
})
return <Root onClick={this.sharedMethod} shared={this.state.shared}/>
}
}
You can use React.createRef for this purpose.
Below is an example code.
import Speech from './Speech';
type Props = {}
type States = {}
export default class SpeechIOS extends Component<Props, States> {
constructor(props: Props) {
super(props);
this.speech = React.createRef();
}
// render
render() {
...
return (<Speech ref={this.speech} team="1" onSpeechResults={this.onSpeechResults}></Speech>);
}
sayHello() {
console.log("Hello!!");
}
// I want that Speech Component call this onSpeechResults function
onSpeechResults(result: string) {
this.setState({ ...});
let temp = this.speech.current.getMatchedSpell(... ); // which is in Speech Component
this.sendCommand(10, 100 ... ); // which is in Speech Component
this.sayHello(); // which is in SpeechIOS component only
...other things..
};
}
I am looking for the way of passing state data from A.js to B.js
so I want to retrieve "DEF " as title[1] from A.js to B.js
A.js
export default class A extends Component {
constructor(props) {
super(props)
this.state = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<Text>{this.state.title[1]}</Text>)}
module.exports = A;
B.js
import {A} from "./A"
export default class B extends Component {
render(){return(<Text>{A.state.title[1]?? // not sure about this part
}</Text>)}
}
custom for you :
import {B} from './B'
export default class A extends Component {
constructor(props) {
super(props)
this.state = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<B titlePropForB={this.state.title})}
module.exports = A;
export default class B extends Component {
render(){
console.log(this.props.titlePropForB);
return(<Text>{this.props.titlePropForB[1]}</Text>)}
}
You need to use props to pass data between components.
Add the props in your parent you want to pass.
export default class A extends Component {
constructor(props) {
super(props)
this.props = {
title: ["ABC","DEF","GHI"]
};
}
render(){return(<Text>{this.props.title}</Text>)}
module.exports = A;
Access them in the child.
import {A} from "./A"
export default class B extends Component {}
render(){return(<Text>{this.props.data[1]}</Text>)}}
This is a good reference discussing the differences between props and state.
refer to my answer here Passing Props to Screens in React Native .
passing props within different components has an alternative way, that is by using Redux library for managing app state. it is simple and easy to understand.
I'm learning React and I'm not sure how to setup this pattern. It could be something really easy I'm just missing.
I have a main component that controls state. It has all of the functions to update state and passes these down to child components via props. I've simplified the code to focus on one of these functions.
Here's the component now, all works as it should:
ManageMenu.js
import React from 'react'
class ManageMenu extends React.Component {
constructor() {
super()
this.toggleEditing = this.toggleEditing.bind(this)
// Set initial state
this.state = {
menuSections: []
}
}
toggleEditing(id) {
const menuSections = this.state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
this.setState({ menuSections })
}
render() {
return (
...
)
}
}
export default ManageMenu
The toggleEditing is passed via props to a child component that uses it to render an editing form if the edit button is clicked.
I have about 10 of these different functions in this component and what I would like to do is move them to an external lib/methods.js file and then reference them. Below is the code I would like to have, or something similar, but React doesn't like what I'm doing. Throws a syntax error:
Failed to compile.
Error in ./src/components/ManageMenu.js
Syntax error: Unexpected token
toggleEditing(id, menuSectionId, this.state, this)
Here is what I would like to do...
lib/methods.js
const toggleEditing = function(id, state, that) {
const menuSections = state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
that.setState({ menuSections })
}
module.exports = {
toggleEditing
}
And then in my component:
ManageMenu.js
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
// Set initial state
this.state = {
menuSections: []
}
}
toggleEditing(id, this.state, this)
render() {
return (
...
)
}
}
export default ManageMenu
Any help is appreciated, thanks!
Thanks to #Nocebo, the answer on how to externalize functions is here:
Externalise common functions in various react components
In my particular situation,
I need to remove the “floating” toggleEditing(id, this.state, this) call in the middle of nowhere. Update: This error happens “because it is invoking a method within a class definition.” (see Pineda’s comment below)
Remove the leading this. on the right side of the this.toggleEditing statement in constructor()
Update the function in lib/methods.js to remove the state and that variables since its bound to this in the constructor()
See updated code below.
ManageMenu.js
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
this.toggleEditing = toggleEditing.bind(this)
// Set initial state
this.state = {
menuSections: []
}
}
render() {
return (
...
)
}
}
export default ManageMenu
lib/methods.js
const toggleEditing = function(id) {
const menuSections = this.state.menuSections
menuSections.map(key => (key.id === id ? key.details.editing = id : ''))
this.setState({ menuSections })
}
module.exports = {
toggleEditing
}
You're error arises because you are invoking toggleEditing in your ManageMenu.js class definition rather than defining a function.
You can achive what you want by setting a local class member this.toggleEditing to the bound function returned by the .bind method and do so within the constructor:
import React from 'react'
import { toggleEditing } from '../lib/methods'
class ManageMenu extends React.Component {
constructor() {
super()
this.state = {
menuSections: []
}
// bind external function to local instance here here
this.toggleEditing = toggleEditing.bind(this);
}
// don't invoke it here, bind it in constructor
//toggleEditing(id, this.state, this)
render() {
return (
...
)
}
}
export default ManageMenu
I m actually learning reactjs and I m actually developping a little TODO list, wrapped inside of a "parent component" called TODO.
Inside of this parent, I want to get the current state of the TODO from the concerned store, and then pass this state to child component as property.
The problem is that I dont know where to initialize my parent state values.
In fact, I m using ES6 syntax, and so, I dont have getInitialState() function. It's written in the documentation that I should use component constructor to initialize these state values.
The fact is that if I want to initialize the state inside of my constructor, the this.context (Fluxible Context) is undefined actually.
I decided to move the initialization inside of componentDidMount, but it seems to be an anti pattern, and I need another solution. Can you help me ?
Here's my actual code :
import React from 'react';
import TodoTable from './TodoTable';
import ListStore from '../stores/ListStore';
class Todo extends React.Component {
constructor(props){
super(props);
this.state = {listItem:[]};
this._onStoreChange = this._onStoreChange.bind(this);
}
static contextTypes = {
executeAction: React.PropTypes.func.isRequired,
getStore: React.PropTypes.func.isRequired
};
componentDidMount() {
this.setState(this.getStoreState()); // this is what I need to move inside of the constructor
this.context.getStore(ListStore).addChangeListener(this._onStoreChange);
}
componentWillUnmount() {
this.context.getStore(ListStore).removeChangeListener(this._onStoreChange);
}
_onStoreChange () {
this.setState(this.getStoreState());
}
getStoreState() {
return {
listItem: this.context.getStore(ListStore).getItems() // gives undefined
}
}
add(e){
this.context.executeAction(function (actionContext, payload, done) {
actionContext.dispatch('ADD_ITEM', {name:'toto', key:new Date().getTime()});
});
}
render() {
return (
<div>
<button className='waves-effect waves-light btn' onClick={this.add.bind(this)}>Add</button>
<TodoTable listItems={this.state.listItem}></TodoTable>
</div>
);
}
}
export default Todo;
As a Fluxible user you should benefit from Fluxible addons:
connectToStores.
The following example will listen to changes in FooStore and BarStore and pass foo and bar as props to the Component when it is instantiated.
class Component extends React.Component {
render() {
return (
<ul>
<li>{this.props.foo}</li>
<li>{this.props.bar}</li>
</ul>
);
}
}
Component = connectToStores(Component, [FooStore, BarStore], (context, props) => ({
foo: context.getStore(FooStore).getFoo(),
bar: context.getStore(BarStore).getBar()
}));
export default Component;
Look into fluxible example for more details. Code exсerpt:
var connectToStores = require('fluxible-addons-react/connectToStores');
var TodoStore = require('../stores/TodoStore');
...
TodoApp = connectToStores(TodoApp, [TodoStore], function (context, props) {
return {
items: context.getStore(TodoStore).getAll()
};
});
As a result you wouldn't need to call setState, all store data will be in component's props.
I'm using ES6 classes with Babel. I have a React component that looks like this:
import { Component } from 'react';
export default class MyReactComponent extends Component {
getInitialState() {
return {
foo: true,
bar: 'no'
};
}
render() {
return (
<div className="theFoo">
<span>{this.state.bar}</span>
</div>
);
}
}
It doesn't look like getInitialState is being called, because I'm getting this error: Cannot read property 'bar' of null.
The developers talk about ES6 class support in the Release Notes for v0.13.0. If you use an ES6 class that extends React.Component, then you should use a constructor() instead of getInitialState:
The API is mostly what you would expect, with the exception of getInitialState. We figured that the idiomatic way to specify class state is to just use a simple instance property. Likewise getDefaultProps and propTypes are really just properties on the constructor.
Code to accompany Nathans answer:
import { Component } from 'react';
export default class MyReactComponent extends Component {
constructor(props) {
super(props);
this.state = {
foo: true,
bar: 'no'
};
}
render() {
return (
<div className="theFoo">
<span>{this.state.bar}</span>
</div>
);
}
}
To expand a bit on what it means
getDefaultProps and propTypes are really just properties on the constructor.
the "on the constructor" bit is weird wording. In normal OOP language it just means they are "static class variables"
class MyClass extends React.Component {
static defaultProps = { yada: "yada" }
...
}
or
MyClass.defaultProps = { yada: "yada" }
you can also refer to them within the class like:
constructor(props) {
this.state = MyClass.defaultProps;
}
or with anything you declare as a static class variable. I don't know why this is not talked about anywhere online with regards to ES6 classes :?
see the docs.