There're 2 ways to define a React component.
First one is like below.
class SomeComponent extends React.Component {
constructor (props) {
super(props)
this.state = {
someState: false
}
this._handleOnChangeState = this._handleOnChangeState.bind(this)
}
_handleOnChangeState (e) {
this.setState({ someState: e.target.value })
}
....
}
Second one is like below.
class SomeComponent extends React.Component {
state = {
someState: false
}
_handleOnChangeState = (e) => {
this.setState({ someState: e.target.value })
}
....
}
These two codes are the same function, but I guess there's some different something like memory usage or etc.
Can someone make it clearly? Thanks in advance!
This is a new proposal (class fields) for ES which is in stage 3 right now. To run a code written in this way you need a transpiler like Babel and an appropriate plugin.
Before transpile:
class A {
static color = "red";
counter = 0;
handleClick = () => {
this.counter++;
}
}
After transpile (with stage 2 on Babel Repl):
class A {
constructor() {
this.counter = 0;
this.handleClick = () => {
this.counter++;
};
}
}
A.color = "red";
In addition to the official proposal 2ality blog post is a good source to see what are the details.
Here is a reddit post if you have time to read the discussion storm what is the reason behind this proposal :)
The arrow function here is a different story. You can use instance properties without constructor and mix your code with standard functions. But when you want to use something like that this won't work:
class App extends React.Component {
state = { bar: "baz"}
foo() { console.log(this.state.bar) };
render() {
return <div><button onClick={this.foo}>Click</button></div>;
}
}
We need to bind our function in somehow like:
return <div><button onClick={this.foo.bind(this)}>Click</button></div>
But, binding our function in a JSX prop is no so good since it will create our function in each render.
One way to do this nicely bind in our constructor:
constructor(props) {
super(props);
this.foo = this.foo.bind( this );
}
But, if I have to write a constructor what is the point? This is why you see arrow functions everywhere where we define the classes like your second example. No need to bind to function thanks to arrow functions. But it is not directly related to this new proposal I think.
The first one is the traditional approach and the second one is when you babel-transform-class-properties plugin.
In the second type babel does the same thing under the hood, therefore it is a matter of convenience.
Related
I'm new to react and I can not understand the difference between these two methods in a class
doSomething=()=>{
console.log("Something")
}
and
doSomething() {
console.log("Something")
}
Both looks like they do the same thing
Once again this has been introduced in new ES7 syntax. You can read more about it here
https://www.reactnative.guide/6-conventions-and-code-style/6.4-es7-features.html
Basically in old ES6 we had to write classes and binding methods like this (copied from the documentation)
class SomeComponent extends Component {
_incrementCounter() {
this.setState({count: this.state.count+1})
}
constructor() {
this._incrementCounter = this._incrementCounter.bind(this);
}
...
}
In new ES7 you can simply use arrow function
class SomeComponent extends Component {
_incrementCounter = () => {
this.setState({count: this.state.count+1})
}
...
}
It is up to you what you gonna use. Both ways are ok to use but as you can see ES7 syntax is much shorter and easier to read
doSomething=()=>{
console.log("Something")
}
The above one is using fat arrow functions. You can access this (the class instance) inside this function without binding.
The second one is just defining a function. You will not have access to this here. You cannot use this inside that function. To use this you need to bind the function in constructor or in some other place
Eg;
this.doSomething = this.doSomething.bind(this);
More on this keyword
This is not quite so React specific but it does have implications when passing these functions around to other contexts.
Classes in ES6 don't require binding to allow the use of this within their methods, for example the following is perfectly valid:
class TestClass {
constructor() {
this.variable = 'a variable';
}
method() {
console.log(this.variable)
}
}
const thing = new TestClass();
thing.method(); // will output 'a variable'
The reason you would specifically want to use an arrow function is so you can pass this function down to a component as a prop or use it as part of a button action. Once you have passed the method reference away it no longer has access to this.
class TestComponent extends Component {
constructor() {
this.variable = 'a variable';
}
method() {
console.log(this.variable)
}
render() {
return <AnotherComponent method={this.method} />
}
}
Calling this.method from inside <AnotherComponent> will produce an error. This is where the arrow function comes in.
class TestComponent extends Component {
constructor() {
this.variable = 'a variable';
}
method = () => {
console.log(this.variable)
}
render() {
return <AnotherComponent method={this.method} />
}
}
method now uses an arrow function and 'lexically binds' this which basically means it takes its this from its surrounding context, in this case the class (component) it has been defined in.
I hope I am capable to explain my question in a understandable way.
I am very new to React! And I am not familiar with how some things get done yet.
I have a GamePage component. On this page I am rendering a GameRound component.
When the game starts, a new "game round" starts. After the game round has finished, I want a second and then a third game round to start. This means I need to get kind of a "new" GameRound. The GamePage should remain.
While writing this, I got an idea of how this could be achieved: In my gameRoundFinished() event, I could reset the state of the GameRound. But is that already the most elegant and especially the Reactive way doing this?
Thanks in advance.
Some code as requested...
GamePage.js
export class GamePage extends React.Component {
constructor(props) {
super(props);
// Set game configuration and data
this.state = {
gameId: props.match.params.gameId,
settings: {
categories: ['Stadt', 'Land', 'Fluss'],
countdown: 5
},
activePlayers: {},
game: null
};
// Open socket
this.socket = openSocket('http://localhost:3000');
this.socket.emit('join', this.state.gameId);
this.socket.on('updatePlayers', players => {
this.state.activePlayers = players;
this.setState(this.state);
});
this.socket.on('startGame', () => {
this.state.game = {
rounds: []
};
this.state.game.rounds.push({
});
this.setState(this.state);
});
}
onClick = () => {
this.socket.emit('ready');
}
render() {
if (this.state.game) {
return (
<GameRound socket={this.socket} config={this.state}></GameRound>
);
} else {
return (
<div>Currently {this.state.activePlayers.length}/3
<Button onClick={this.onClick}>klick</Button></div>
);
}
}
}
GameRound.js
export class GameRound extends React.Component {
// too much code that is irrelevant for the question
render() {
return ...
}
}
A very simple way to "reset" a component (and re-run the constructor) is to use the key prop.
See https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
In your case: be sure to provide a new key at every round change: something like:
<GameRound key={roundId} socket={this.socket} config={this.state}></GameRound>
Before other things it is necessary to clean the code:
To call functions together with synthetic events in the render method:
<Button onClick={this.onClickEvent()}>click</Button></div>
To use this.setState({ a: 123 }) for changing this.state object, but not to use this.state itself for changing the state.
Good example:
this.setState({ activePlayers: players });
Bad examples
this.setState(this.state);
this.state.game = { rounds: [] };
this.state.game.rounds.push({ ... });
You may want to use socket.on() function to change your state as follows:
socket.on(){
...
this.setState({ activePlayers: players });
}
<Button onClick={this.socket.on()}>click</Button></div>
One solution is to design GameRound so that it accepts props to determine its behavior. Then to "reset", you just pass the appropriate values for the props to set it to the new round.
Suppose I have a container component for handling app logic which has a lot of methods:
class ScreenContainer extends React.Component
{
state = {
inputs: { /* various properties for input values */ },
thingyActive: false,
someList: ["thing1", "thing2"],
// ...etc.
};
handleInputChange = e => {
const { name, value } = e.target;
this.setState(prevState => ({
inputs: { ...prevState.inputs, [name]: value }
}));
};
toggleThingy = () => this.setState(prevState => ({
thingyActive: !prevState.thingyActive
}));
coolMethod = () => { /* Do cool stuff */ };
boringMethod = () => { /* Do boring stuff */ };
// ...more methods...
}
I need ALL of these methods to be accessible to inner components. I'll use a Context provider in this example, and we'll just say that the context gets consumed by various nested presentational components making up a screen in the application.
const ScreenContext = React.createContext();
To pass methods either down to a child component or into a context provider value, it seems you always end up having to do something like below (note that I'm lifting the "actions" into state in this example per the advice given in the React documentation).
class ScreenContainer extends React.Component
{
constructor()
{
super();
this.state = {
// ...same state as before, plus:
actions: {
handleInputChange: this.handleInputChange,
toggleThingy: this.toggleThingy,
coolMethod: this.coolMethod,
boringMethod: this.boringMethod,
everySingleOtherMethod: this.everySingleOtherMethod,
// ...on and on
}
};
}
// ...same methods as before...
render()
{
return (
<ScreenContext.Provider value={this.state}>
{this.props.children}
</ScreenContext.Provider>
);
}
I was looking for a way to avoid passing them all one by one. A possible solution I found involves using a getter and looping through the class instance properties like so:
get allMethods()
{
let output = {};
for (var prop in this)
{
if (this.hasOwnProperty(prop) && typeof this[prop] === "function")
output[prop] = this[prop];
}
return output;
}
Then I can just do:
// (in ScreenContainer constructor)
this.state = {
// ...state,
actions: this.allMethods
};
The getter code could also be extracted out into a utility function for reuse in other container-type components if needed. Obviously, this is only worthwhile if there are a ton of methods to be passed down.
It seems simple enough and appears to work just fine as long as it's done in the contructor. Is there anything crazy about this? Is it bad practice in any way, or does it have any potential side effects I'm not aware of? Is there maybe a better way I'm missing?
EDIT
I've updated the example to be closer to my real code; it now shows what kinds of things the methods might do and uses a Context setup rather than passing the methods down as props to a single child component.
If a class doesn't maintain a state, and class methods are supposed to be used separately as helper functions, they shouldn't be a part of the class, let alone class component. A class acts as namespace in this case. In modern JavaScript, modules are used as namespaces. It can be:
export const coolMethod = () => { /* Do cool stuff */ };
export const coolerMethod = () => { /* Do even cooler stuff */ };
export const boringMethod = () => { /* Do boring but necessary stuff */ };
ScreenContainer component is an example of 'smart' container component. It's always preferable to list passed functions explicitly rather than pass them all automatically. ScreenContainer may get private methods at some point. And there should be a guarantee that lifecycle hooks won't be passed accidentally, too.
If it is supposed to have a single child, it can be applied as higher-order component:
const withScreen(Comp) => {
return class ScreenContainer extends React.Component {
...
render() {
return <Comp handleInputChange={this.handleInputChange} /* ... */ />;
}
}
}
In this particular case render can be distinguished from passed functions because the latter are instance methods (arrow functions). While this kind of magic generally isn't recommended because it may cause problems and won't work properly for private methods, it can be shortened to:
render() {
const fns = {};
for (const method of Object.keys(this)) {
if (typeof this[method] === 'function')
fns[method] = this[method];
}
return <Comp {...fns} {...this.props} />;
}
For multiple children, ScreenContainer children could be traversed to add props in a similar way.
For indirect children, context API can be used to pass functions.
While it's possible to pass ScreenContainer this to children, this isn't recommended because this breaks the encapsulation and contradicts the principle of least privilege.
One way I've done this is to instantiate a new instance in the constructor of the child component like this:
class ChildComponent extends Component {
constructor(props) {
super(props);
this.Container = new MyContainer();
}
Then you can use any methods like:
this.Container.coolMethod()
Edit
I misunderstood. I've only done this by creating a helper class that you instantiate, not a component. It is helpful when you have methods you want to use in multiple components without having to pass all your methods as props through the component tree.
I am refactoring an es6 class based React component that uses the normal constructor, and then binds methods, and defines state/attributes within that constructor. Something like this:
class MySpecialComponent extends React.Component {
constructor(props) {
super(props)
this.state = { thing: true }
this.myMethod = this.myMethod.bind(this)
this.myAttribute = { amazing: false }
}
myMethod(e) {
this.setState({ thing: e.target.value })
}
}
I want to refactor this so that I am autobinding the functions, and using property initializers for the state and attributes. Now my code looks something like this:
class MySpecialComponent extends React.Component {
state = { thing: true }
myAttribute = { amazing: false }
myMethod = (e) => {
this.setState({ thing: e.target.value })
}
}
My question is, do I still need the constructor? Or are the props also autobound? I would have expected to still need the constructor and included super(props), but my code seems to be working and I'm confused.
Thanks
From my understanding, you don't need to type out a constructor at all when using class properties (as in your second code example). The accepted answer states that you do need one if you "need to reference the props in your initial state object," but if you're using said class properties, then you're probably using Babel to transpile it, in which case a constructor is used, it's just being done behind the scenes. Because of this, you don't need to add a constructor yourself, even if you are using props in state.
See this aricle for better examples and a better explanation.
You don't need an explicitly defined constructor unless you need to reference the props in your initial state object.
You don't need to define a constructor explicitly , and then do super(props).You can access the props as in the example below. i.e. 'prop1'
class MySpecialComponent extends React.Component {
state = {
thing: true ,
prop1:this.props.prop1
}
myAttribute = { amazing: false }
myMethod = (e) => {
this.setState({ thing: e.target.value })
}
render(){
console.log(this.state.prop1);
return(
<div>Hi</div>
);
}
}
ReactDOM.render(<MySpecialComponent prop1={1}/> , mountNode);
Is there a way to avoid the boilerplate when using ES6 with react 0.14?
Until now I didn't have to worry about my function to be bound to the Component I created but that is no longer (why?!?) the case and the Component is only bounded to the Component super class (If I understood the errors correctly).
So what I really need to do each time I create a new class is to add this code to the constructor:
class CustomComp extends React.Component {
constructor() {
super();
this.newFunction = this.newFunction.bind(this);
}
newFunction(){
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction}>Click</button>
}
}
So if I wont bind newFunction it will fail (no props, state or anything).
Is there a way to work around this?
From the React documentation:
No Autobinding
Methods follow the same semantics as regular ES6 classes, meaning that
they don't automatically bind this to the instance. You'll have to
explicitly use .bind(this) or arrow functions =>.
So, no there is not an automatic way to bind all methods that is new in 0.14. But, as the documentation suggests, you can:
1) use arrow functions (However, if you are using Babel, you need stage 0):
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction = () => {
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction}>Click</button>
}
}
2) you can bind in place:
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction() {
console.log('This is user defined function');
}
render() {
return <button onClick={this.newFunction.bind(this)}>Click</button>
}
}
3) you can use an arrow function in place (which is like a bind):
class CustomComp extends React.Component {
constructor() {
super();
}
newFunction() {
console.log('This is user defined function');
}
render() {
return <button onClick={() => this.newFunction()}>Click</button>
}
}
I tend to use number 2 & 3 if I only have a 1-2 methods. Number 1 is good, but you have to remember the syntax for every method definition. If I have a lot of methods, I do tend to bind in the constructor.