Higher Order Function and Classes in different files - reactjs

Currently using react-native and working with High Order functions. I have some presentation components that I am using and I currently have a HOC Container that handles some of the layout properties.
I now realize that I want to have multiple containers that will be different configurations of the same class. For that I made a class in a different file, however the problem is that I can't seem to pass my components to the class with the arrow function. I am pretty sure I am missing something really trivial.
Here is part of the code to understand the problem:
BaseContainer:
export default class BaseContainer extends Component {
render(){
<Wrapped/>
}
}
HOC Components (Ignore the 2 export default, these are 2 different modules):
export default RegularContainer = (Wrapped) => BaseContainer;
export default MessageContainer = (Wrapped) => class extends BaseContainer {
constructor(props){
super(props);
this._borderStyle = 'containerLeftBorder';
}
}
The error I am getting is "Can't find variable: Wrapped" in BaseContainer, which is understandable but I cannot figure out how to pass the Wrapped variable when the class is in another module.
This was working fine if I define the content of the BaseContainer class in the same file.

Well, I "solved" it but it seems a little ugly. I'd love to hear some feedback. Otherwise I'll close this as the answer:
export default RegularContainer = (Component) => class extends BaseContainer {
constructor(props){
super(props);
this._Wrapped = Component;
}
}
export default MessageContainer = (Component) => class extends BaseContainer {
constructor(props){
super(props);
this._borderStyle = 'containerLeftBorder';
this._Wrapped = Component;
}
}

Related

use lifecycle in child component in react

I'm implementing an inherit action to archive my goal.
My goal is to call a lifecycle event in child component instead of parent one. That's what I can do in C# .net. How can I archive it? Is there any difference?
When I call lifecycle event in parent component, it works fine
Here's my code
class A extends React.Component {
render() {
return (
//......
)
}
}
class B extends A {
componentDidMount() {
console.log('componentDidMount') // didn't log here
}
componentDidUpdate(prevProps) {
console.log('componentDidUpdate:', prevProps) // didn't log here
}
}
Thanks all
This is quite tricky.
In JavaScript, a class cannot extend from multiple classes, which is also known as “multiple inheritance”. In JavaScript, objects can only be associated with a single prototype, and extending multiple classes would mean that an object associates with multiple prototypes, which is not possible.
Also, to have lifecycle methods, the class should extend React.Component.
So you can either extend React.Component or Component A.
Multiple inheritance doesn't allow in javascript by the time I'm writing this. And life-cycle event will work only if you extends React.Component. So, you should not extends another component in react. The recommended way is - use composition pattern instead of inheritance.
import B from './B';
class A extends React.Component {
render() {
return (
<B />
//.......
)
}
}
class B extends React.Component {
componentDidMount() {
console.log('componentDidMount')
}
componentDidUpdate(prevProps) {
console.log('componentDidUpdate:', prevProps)
}
}
Now it will work. Hope you will figure out the problem. For more please visit reactjs composition vs inheritance
the trick is to use super.componentDidMount() in the child component
componentDidMount() {
super.componentDidMount();
console.log('componentDidMount')
}
here you can find the working code
https://codesandbox.io/s/crazy-snow-1k9t2?file=/src/App.js

Passing Typescript generics on React component through connect from react-redux

I'm trying to use generics in a component that gets passed through react-redux's connect. Here's a stripped down version:
export default class ItemBar<T> extends React.PureComponent{
// ...
}
When I use ItemBar with no connect, it looks like this:
export default class Component extends React.PureComponent {
render() {
return <ItemBar<number> />;
}
}
This works properly. When I "connect" the ItemBar class as such:
class ItemBar<T> extends React.PureComponent {
// ...
}
export default connect()(ItemBar);
I now get Expected 0 type arguments, but got 1. from Typescript. I think this is because the connect (and probably any other higher component) doesn't pass through the generics. Is there any way I can get this to work?
class ItemBar<T> extends React.PureComponent {
// ...
}
const connected = connect()(ItemBar);
export class ItemBar<T> extends connected {}
You may have to disable tslint at last line if you have one class rule per file.

How to solve TypeScript typing issue with component composition

React promotes composition over inheritance, but I
I have a React component in TypeScript that should host certain kinds of React components. For example, let's say I have a MenuBar component, and it takes an array of various MenuBarItem components it can host as a prop. I want to force all menu bar items to have the same root element structure, so they must be rendering the MenuBarItem component at the root.
The problem I have is that, if I was using inheritance, I can achieve this by defining the prop as something like items: MenuBarItem[], but I cannot figure this out how to enforce this with composition.
By using composition, I created FooMenuBarItem and BarMenuBarItem, and let them render the MenuBarItem internally (See below). So, they don't share the common base class that I can use as a type.
How would I solve this typing problem with composition?
export class MenuBarItem extends React.Component<{}, {}> {
render() { return /*...*/; }
}
export class FooMenuBarItem extends React.Component<{}, {}> {
render() { return <MenuBarItem>Foo</MenuBarItem>;}
}
export class BarMenuBarItem extends React.Component<{}, {}> {
render() { return <MenuBarItem>Bar</MenuBarItem>;}
}
export interface MenuBarProps {
items: MenuBarItem[]; // This would only work if I use inheritance to get the common base type!
}
export class MenuBar extends React.Component<MenuBarProps , {}> {
render() {
<div>{this.props.menuBarItems}</div>
}
}
I found a similar question here, but with no answers.
I think the short answer is that there is no way to declare the exact type you want (I would be interested to hear otherwise!).
To be honest I would normally just go with:
export interface MenuBarProps {
items: React.ReactNode;
}
And rely on consumers of the code to do the sensible thing.
The only other thing I would consider is that perhaps you could use:
export interface MenuBarProps {
items: React.Component<MenuItemProps>;
}
This would only make sense if the various different menu item components all shared a set of common props.

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.

Subclassing react components, HOC or classic OO?

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)

Resources