Why are there different syntax of methods inside extending React.Component? - javascript

I notice that inside class ExampleComponent extends React.Component {...} there are different ways of defining methods, with the former being a declaration for methods that is part of React and the latter being expressions for your own methods. Why is this? Why aren't they both in the same format?
componentDidMount() {
...
}
vs.
myMethod = () => {
...
}

This one goes to prototype
fnProto() {
}
This one is experimental and goes directly to instance having this always refering to the instance.
fnInstance = () => {}
Translating to ES5
class Cmp {
fnProto() {
console.log('goes to proto')
}
fnInstance = () => {
console.log('goes to instance')
}
}
Will be roughly equivalent to
function Cmp() {
this.fnInstance = (function() {
console.log('goes to instance')
}).bind(this)
}
Cmp.prototype.fnProto = function() {
console.log('goes to proto')
}

When you have
componentDidMount() {
...
}
it is a lifecycle function and this inside it is automatically bound by default to the React Component context.
However when you define your own function, this inside it will refer to the content of the function itself. However if you define it using an arrow function like
myMethod = () => {
...
}
this keyword inside it will refer to the parent context which in this case is the React Component context.
Check this article on Arrow function

Related

How to decorate a Web Component class

I'm creating a #Component decorator that intercedes the constructor of a class to carry out some work after construction. As can be seen in the following code, the work is implemented in an init method.
export function Component (Cls) {
function Class (...args) {
let self = new Cls (...args); // (1)
init (self, ...args);
return self;
}
Class.prototype = Cls.prototype;
return Class;
}
When I test this code on a regular class all works fine. This is a working example:
class Base { ... }
#Component
class Core extends Base {
constructor () {
super (); // init is invoked
}
fx () { console.log ('Core.fx') }
fy () { console.log ('Core.fy') }
}
Nevertheless, when I try to decorate a web component a TypeError: Illegal constructor message is obtained.
#Component
class Core extends HTMLElement {
constructor () {
super ();
}
fx () { console.log ('Core.fx') }
fy () { console.log ('Core.fy') }
}
customElements.define ('x-core', Core);
let coreX = document.createElement ('x-core');
document.body.appendChild (coreX);
I realise the problem is that HTMLElement's do not support direct construction through new operator - see (1) on first listing - but I need a procedure to decorate constructor of any class even though they are custom elements.
Some Idea?
Working Settings: Chrome 68 ยท Babel 7.0.0-beta.51 with babel-plugin-transform-decorators-legacy
You can return a class to avoid direct new.
function Component(cls) {
class c extends cls {
constructor() {
super()
console.log(this)//init
}
}
return c
}

React - Setting component state using a function outside of state, is it wrong?

Is it wrong to use setState in a function outside of the React component?
Example:
// myFunction.js
function myFunction() {
...
this.setState({ ... })
}
// App.js
import myFunction from './myFunction
class App extends Component {
constructor() {
super()
this.myFunction = myFunction.bind(this)
}
...
}
I'm not sure the way you're binding will actually work. You could do something like:
export const getName = (klass) => {
klass.setState({ name: 'Colin'})
}
then
class App extends Component {
state = {
name: 'React'
};
handleClick = () => {
getName(this);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<button onClick={this.handleClick}>change name</button>
</div>
);
}
}
Working example here.
So the only reasons to do this is if you are reducing repeated code, e.g. two components use the same logic before calling this.setState, or if you want to make testing easier by having a separate pure function to test. For this reason I recommend not calling this.setState in your outside function, but rather returning the object you need so it can you can call this.setState on it.
function calculateSomeState(data) {
// ...
return { updated: data };
}
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = calculateSomeState(props.data);
}
handleChange = (e) => {
const value = e.target.value;
this.setState(calculateSomeState({ ...props.data, value }));
}
}
It looks like a bug waiting to happen... If you want to use an external function to set state, you can use the alternative syntax provided by React:
this.setState((prevState, props) => {
return updatedState; //can be a partial state, like in the regular setState
});
That callback can easily be extracted to an external function and it's guaranteed to work
It is not wrong, the function is never called outside the component. This is a mix-in technique. bind isn't needed, as long as the function isn't used as a callback. In this case myFunction is same among all instances, a more efficient way would be:
class App extends Component {}
App.prototype.myFunction = myFunction;

'this' returns global scope in Keyboard listeners events

I'm trying to listen for keyboard on/off events, using the Keyboard module from 'react-native', the listeners work but on the receiver I can't access the state because 'this' references the global score instead of this component's 'this'.
export default class SignupEmailScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
emailText: '',
continueTextFadeAnim: new Animated.Value(0.4),
continueText: emailInvalidText,
bottomLocation: Layout.window.height,
};
console.log(this)
}
componentWillMount() {
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow);
this.keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', this._keyboardDidHide);
}
componentWillUnmount() {
this.keyboardDidShowListener.remove();
this.keyboardDidHideListener.remove();
}
_keyboardDidShow(endCoordinates) {
console.log(this)
}
_keyboardDidHide() {
}
...
}
The first console.log(this) in the constructor logs as excepted:
But the second one in the keyboardDidShow() logs this (it goes on):
Question: How can I change the state in the keyboard listeners?
Is this because the keyboard listeners functions may be actually static?
You want to bind the scope when passing a method reference and calling that method in some other context. You can use bind like this:
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', this._keyboardDidShow.bind(this));
Alternatively, you can use arrow functions that preserves the context of "this" keyword.
this.keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
this._keyboardDidShow(<method params>)
});

how come class methods don't behave normally with react components?

Given a component like this :
class App extends Component {
state = {}
sayHello() {
// 'this' is undefined here... It should point to the component instance
}
render() {
return (
<div onClick={this.sayHello}>
clickMe
</div>
);
}
}
how come sayHello can't access this ? That is not the expected behavior of an ES6 class.
What am I missing out on ?
In javascript this depends on how you call the method. This can be changed by binding this inside a method. To illustrate what is happening in your example:
class App {
sayHello() {
console.log(this)
}
}
const a = new App()
a.sayHello() // A {}
const callback = a.sayHello // what you are passing as a callback to onClick
callback() // undefined
To fix this problem you need to bind this in sayHello function. You can use class properties to achieve this. This is still an experimental feature of javascript but seems to be a community accepted way of binding this for React components.
class App {
sayHello = () => {
console.log(this)
}
}
callback() // A {}
div it self is another Class so when calls your sayHello inside himself it will change this.
All you have to do is bind this to sayHello or call it via ES6 arrow function.
<div onClick={ this.sayHello.bind(this) }>
or
<div onClick={ (event) => this.sayHello(event) }>

How to access props in other functions of imported modules

Let say i created a basic modules with simple functions like helper.js
export function HelloChandu() {
//How to access navigator props from here.
}
export function HelloTester() {
HelloChandu();
}
Then I imported this module in my component as import * as Helper from './helper';
In some element I then called onpress={Helper.HelloTester.bind(this)} So by this now I can access this.props in HelloTester function but I can not access this.props in HelloChandu function.
Question : How can I access this.props from any function in my helper.js module ? Like if there are 10-20 functions , and i don't have to pass as parameters around.
Thank You
I am afraid that if you want to access this.props in one of your functions you will need to pass this explicitily or to bind all the functions to the current this before using them.
There are several ways to do so.
function HelloChandu() {
alert(this.props);
}
function HelloTester() {
HelloChandu.apply(this);
// HelloChandu.call(this);
// HelloChandu.bind(this)();
// this::HelloChandu(); // Experimental!
}
const obj = {
props: 'my props'
}
HelloTester.bind(obj)()
An alternative way would be to wrap all the functions in another function.
function helpers() {
const functions = {
HelloChandu: () => {
alert(this.props);
},
HelloTester: () => {
functions.HelloChandu();
}
};
return functions;
}
const obj = {
props: 'my props'
}
helpers.call(obj).HelloTester();
1.You can persist props to AsyncStorage, whenever you need, you can access this props.
2.Or if you are familiar with closure, you can do like this :
function Helper(ps) {
function test() {
console.log(ps.sex);
}
function test2() {
console.log(ps.name);
}
return {
test: test,
test2: test2,
}
}
var obj = Helper({name: 'abc', sex: 'male'});
obj.test();
obj.test2();
then you should export Helper, and import Helper from 'Helper.js'
export function HelloChandu(_this) {
//How to access navigator props from here.
}
export function HelloTester(_this) {
HelloChandu(_this);
}

Categories

Resources