Subclassing react components, HOC or classic OO? - reactjs

I am in the process of writing a React application which is responsible for rending content from an external CMS.
Content is pulled from the CMS into a redux store when the application first loads and is accessed via connected components or props throughout its life-cycle.
I need certain methods to be available across all components making use of CMS'd content.
After researching the "React" way of achieving this, it seems the following way is generally frowned upon whilst using higher order components is viewed as a better practice. Coming from an OO background, I'm struggling to understand why?
For example...
import React, { Component } from 'react';
class CMSComponent extends Component {
constructor(props) {
super(props);
}
mySharedMethod(path) {
// Assume CMS content is located in this.props.content unless otherwise stated
// Please be aware that the actual code has been simplified massively for the sake of the question
return this.props.content[path]
}
}
I now have a base class that all my CMS components can inherit from, like so...
import CMSComponent from './components/cms'
class Page1 extends CMSComponent {
constructor(props) {
super(props);
}
render() {
// mySharedMethod() inherited from base class
return <div>{ this.mySharedMethod(['data', 'intl', 'my-keyed-content']) }</div>
}
}
If anyone could shed any light on why this is considered incorrect I would be extremely grateful.
For reference, I guess the same scenario using HOC would look something like this...
import React, { Component } from 'react'
export default function CMSInject(ChildComponent) {
class CMSComponent extends Component {
constructor(props) {
super(props);
}
mySharedMethod(path) {
return this.props.content[path]
}
render() {
return <ChildComponent {...this.props} {...this.state} /* and some refs */ />
}
}
return CMSComponent
}
...then export the child via the higher order parent component...
import React, { Component } from 'react'
class Page1 extends Component {
constructor(props) {
super(props);
}
render() {
// mySharedMethod() still inherited from base HOC class
return <div>/* CMS BASED CONENT HERE */</div>
}
}
export default CMSInject(Page1)

Related

Can a child component render its parent content only?

I'm extending a parent component that is part of an SDK (AWS Amplify - SignIn), which I have no control over. I only need to make a small change where the input field data will be modified to be lowercase before it's passed to the authentication function.
import React from "react";
import { SignIn } from "aws-amplify-react";
export class CustomSignIn extends SignIn {
constructor(props) {
super(props);
this._validAuthStates = ["signIn", "signedOut", "signedUp"];
}
showComponent(theme) {
return (
<div>My child component that I don't need to render</div>
);
}
}
export default CustomSignIn;
This is more of a general question not specifically related to AWS Amplify, but I'd like to use the existing UI / rendering code from the parent — is there a way to simply display the parent's rendering content and not have any child content?
You can extend a component and use that components render function.
class ComponentFromThirdParty extends React.Component {
render() {
return <div>This is ComponentFromThirdParty</div>;
}
}
class MyComponent extends ComponentFromThirdParty {
componentDidMount() {
console.log(
"MyComponent was mounted and I render the same as ComponentFromThirdParty"
);
}
render() {
return super.render();
}
}
MyComponent will render this when mounted.
This is ComponentFromThirdParty

Simplify this repeating code pattern on class components

I'm currently implementing accessibility (VoiceOver/Talkback) support for my application, and I use AccessibilityInfo.setAccessibilityFocus (see official docs) quite a lot which requires a reactTag, which I can only get by using findNodeHandle as per this answer.
This means that I keep repeating the same pattern involving quite a few function calls, over and over on many different components. I originally tried to move the stored references and the call to set focus to my state manager (using MobX in this case) but I ended up getting a lot of failed findNodeHandle calls because sometime the component had unmounted before the call was made.
This is the gist of what I keep repeating:
import React, {Component} from 'react'
import {
...
findNodeHandle,
AccessibilityInfo
...
} from 'react-native';
class Sample extends React.Component {
constructor(props) {
super(props)
this.accessibilityRef = null;
}
...
componentDidMount() {
this.setAccessibilityFocus()
}
componentDidUpdate() {
this.setAccessibilityFocus()
}
setAccessibilityRef(el) {
this.accessibilityRef = el
}
setAccessibilityFocus() {
if (this.accessibilityRef) {
const reactTag = findNodeHandle(this.accessibilityRef);
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
render() {
return (
<View ref={this.setAccessibilityRef} accessible={true}>
...
</View>
)
}
}
Would it be possible to somehow make something reusable out of this? Maybe a decorator or as a class extension so that I can reuse it?
Yes, you can...
Let, SampleWrapper.js is your wrapper.
import React, {Component} from 'react'
import {
...
findNodeHandle,
AccessibilityInfo
...
} from 'react-native';
export default class SampleWrapper extends Component {
constructor(props) {
super(props)
this.accessibilityRef = null;
}
...
componentDidMount() {
this.setAccessibilityFocus()
}
componentDidUpdate() {
this.setAccessibilityFocus()
}
setAccessibilityRef(el) {
this.accessibilityRef = el
}
setAccessibilityFocus() {
if (this.accessibilityRef) {
const reactTag = findNodeHandle(this.accessibilityRef);
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
render() {
return (
<View ref={this.setAccessibilityRef} accessible={true}>
{this.props.children}
</View>
)
}
}
Now, assume you want to use the above wrapper in your Sample.js
import React, {Component} from 'react'
import SampleWrapper from './path/to/SampleWrapper'
class Sample extends Component {
constructor(props) {
super(props)
}
render() {
return (
<SampleWrapper>
....
</SampleWrapper>
)
}
}
You can put ref s and then you can control it as you expect.
PS: I didn't test this before posting. Hope this will work and help you. If you have any problems regarding this answer or if you want to add something more and you have doubts about how to do it, please just comment on it here.

How to access state of one component into another component in react

import React, { Component } from 'react';
class one extends React.Component
{
constructor()
{
super();
this.state = {
number:26
}
}
render()
{
return(
<div></div>
);
}
}
export default one;
import React, { Component } from 'react';
import one from './one'
class HomePage extends React.Component
{
render()
{
return(
<div>{one.state.number}</div>
);
}
}
export default HomePage;
is it possible to access number state
is there any way to access state of one component into another component?
please suggest me if any solution is present.
As Shubam has explained it, Though I would like to form it as a complete answer
First of all, I would like to let you know that Never Use lowercase letters to name your React Components.So name your component to One instead of one.
Now Comming back to your question:-
No This is not Possible, If your app contains few components then it's better to pass the state object as the props, But if your app contains too many components then better to use predictable state containers like Redux or Flux rather than passing state as props.
So you may apply these changes and I hope You will get What You Desire:-
One Component:-
import React, { Component } from 'react';
import Homepage from './homepage';
class One extends React.Component
{
constructor()
{
super();
this.state = {
number:26
}
}
render()
{
return(
<Homepage data={this.state}/>
);
}
}
export default One;
Homepage Component:-
import React, { Component } from 'react';
class Homepage extends React.Component
{
render()
{
console.log("this is homepage",this.props);
return(
<div>{this.props.data.number}</div>
);
}
}
export default Homepage;
Please Raise Your doubts if any, Or if you find any error in it.

The this keyword is undefined in React base class

I have a basic React app and I'd like to put some commonly used functionality into a base component class and have all my other components inherit from that class to get access to those features. I have this:
export class BaseComponent extends React.Component {
constructor() {
super();
this.commonlyUsedMethod = this.commonlyUsedMethod.bind(this);
}
commonlyUsedMethod() {
let x = this.someValue; // <--- 'this' is undefined here
}
}
export class SomeComponent extends BaseComponent {
onButtonClick() {
super.commonlyUsedMethod();
}
render() {
return whatever;
}
}
The problem is that when I call super.commonlyUsedMethod() from the derived class, this.someValue blows up inside BaseComponent.commonlyUsedMethod() because this is undefined. I'm calling this.commonlyUsedMethod.bind(this); in the BaseComponent constructor, so I'm not sure what's going on.
First of all I (and most of the React dev community) don't recommend you to use inheritance. https://facebook.github.io/react/docs/composition-vs-inheritance.html
Most of the use cases you have you can solve it using Higher Order Components or writing functions in a JS file and importing it.
If you still want to go ahead and do this.
You need to bind the this when you attach the buttonClick listener
export class SomeComponent extends BaseComponent {
onButtonClick() {
super.commonlyUsedMethod();
}
render() {
return <div onClick={this.onButtonClick.bind(this)}>Hello</div>;
}
}
Here is the working example for it. https://www.webpackbin.com/bins/-Knp4X-n1RrHY1TIaBN-
Update: Problem was not with calling super with proper this, problem was with not binding proper this when attaching the onClick listener. Thanks #Mayank for pointing it out.
So I'm not sure if this a Good Practice™, but I can get it to work by calling this.someCommonMethod() instead of super.someCommonMethod(), like this:
export class SomeComponent extends BaseComponent {
constructor() {
super();
this.onButtonClick = this.onButtonClick.bind(this);
}
onButtonClick() {
this.commonlyUsedMethod(); <--- changed 'super' to 'this'
}
render() {
return whatever;
}
}
I'm new enough to React and ES6 not to know if this is how this should work. Any thoughts would be appreciated.

Click event in ReactJS error: imports/ui/ParentComponent.jsx:5:16: Unexpected token (5:16)

I am trying to learn Event in ReactJS.
I created 2 components
ChildComponent is
import React, { Component } from 'react';
// App component - represents the whole app
export default class ChildComponent extends Component {
render() {
return (
<button onClick={this.props.onBannerClick}>Click me!</button>
);
}
}
And ParentComponent is
import React, { Component } from 'react';
import ChildComponent from './ChildComponent.jsx'
// App component - represents the whole app
export default class ParentComponent extends Component {
performMagic: function() {
alert('TAADAH!');
},
render() {
return (
<BannerAd onBannerClick={this.performMagic} />
);
}
}
but I got the error
Errors prevented startup:
While building for web.browser:
imports/ui/ParentComponent.jsx:5:16: Unexpected token (5:16)
Your application has errors. Waiting for file change.
I think the error is from
performMagic: function() {
alert('TAADAH!');
},
But I do know what the error is.
By the way, can anybody recommends me good debug tools for ReactJS?
Because you're using the ES6 syntax you'll have to bind the function to the instance using the following approach.
constructor(props) {
super(props)
this.performMagic = this.performMagic.bind(this)
}
This will allow you to use the this keyword in the onClick call
import React, { Component } from 'react';
import ChildComponent from './ChildComponent.jsx'
// App component - represents the whole app
export default class ParentComponent extends Component {
constructor(props) {
super(props)
this.performMagic = this.performMagic.bind(this)
}
performMagic() {
alert('TAADAH!');
}
render() {
return (
<BannerAd onBannerClick={this.performMagic} />
);
}
}
Need to write:
performMagic () {
alert('TAADAH!');
},
You need to use new sintax for functions, when write class which is new sintax.
EDIT: You can use "React Developer Tools" chrome extension and gaearon "redux-devtools" for development.
You need to use the new ES6 syntax when making your React Component a class. Use
performMagic() {
alert('TAADAH!');
}
make sure you don't put a comma after the function

Resources