extending React.Component class - reactjs

I have recently started working with React and getting understanding of how the framework works,
I have two pieces of code below , just wondering that what's the difference between them (of-course I am not asking about the syntactical differences) and why one of them gives error.
this one works
interface Square {
value:String;
}
class Square extends React.Component<Square,{}> {
}
this one gives following error
[ts] Generic type 'Component' requires 2 type argument(s).
class Square extends React.Component {}
I have seen many examples on the net which extend React.Component for writing new components, I think I am missing something here.

Because you are using TypeScript.
The code you saw on the internet class xxx extends React.Component is just ES6 code.
Here is a simple React code written in TypeScript:
interface SomeProps {
blabla: string;
}
class SomeComponent extends React.Component<SomeProps, any> {
constructor(props: SomeProps) {
super(props);
}
render() {
return <h1>{this.props.blabla}</h1>;
}
}

Related

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.

Property "value" does not exist on type Readonly

I am learning how to develop applications using React as front-end and spring as back-end. While developing a sample application, I encountered an error as follows:
`(26,28): Property "value" does not exist on type 'Readonly<{}>`
This error is generated from my App.tsx file which contains the React Components definition in JSX.
The class component with the state "value" is defined as follows.
class App extends React.component{
constructor(props:any) {
super(props);
this.state = {
value:[],
isLoading:false
};
}
...
25 render(){
26 const value = this.state.value;
27 const isLoading = this.state.isLoading;
...
}
}//End of Class*
I am not understanding what's wrong. Can someone please help me look at it in a different perspective so that this problem can be approached in a different manner?
Did you declare an interface for your state?
Take a look at Hello React and TypeScript which shows an example of using interfaces with React Components.
I suspect you're missing something like this:
interface IMyComponentProps {
someDefaultValue: string
}
interface IMyComponentState {
someValue: string
}
class App extends React.Component<IMyComponentProps, IMyComponentState> {
// ...
}
could you please try this as follow.
class App extends React.component<{},any>{ //your code}
export default class ReactTodoWebpart extends React.Component<IReactTodoWebpartProps,any> {
/**
*
*/
constructor(props:IReactTodoWebpartProps) {
super(props);
this.state={
todoItems:[]
};
this.props.todoClient.getItems().then(resolvedItems=>{
this.setState({
todoItems: resolvedItems,
})
});
}
// I also face this issue and fixed it by using any in the second argument

How to write an abstract class for a component (with extendable state & props)?

I'm trying to write an abstract ReactJS class, then extend it. I'll thus need to extend its props and state (as far as I understand; I'm new to React).
Based on Nitzan's post showing how to extend props from a base class, I made an abstract class Animal:
import * as React from "react";
export interface AnimalProps {
isHibernatory: boolean;
}
export interface AnimalState {
shouldHibernate: boolean;
}
// TS2322: Type '{ shouldHibernate: boolean; }' is not assignable to type 'Readonly<S>'.
export abstract class Animal<P extends AnimalProps, S extends AnimalState>
extends React.Component<P, S> {
constructor(props: P) {
super(props);
this.state = {
shouldHibernate: props.isHibernatory
};
}
}
... And also made a class Cat that extends it:
import * as React from "react";
import {AnimalProps, AnimalState, Animal} from "./Animal";
export interface CatProps extends AnimalProps {
isHairless: boolean;
}
export interface CatState extends AnimalState {
shouldSeekWarmth: boolean;
}
export class Cat extends Animal<CatProps, CatState> {
constructor(props: P) {
super(props);
this.state = {
willHibernate: props.isHibernatory,
shouldSeekWarmth: props.isHairless
};
}
}
However, as commented, the TypeScript compiler throws the error TS2322: Type '{ shouldHibernate: boolean; }' is not assignable to type 'Readonly<S>'. I believe this is because it can't guarantee that S will be readonly once it has extended AnimalState. How else could I write this? Or am I missing a bigger picture?
React prefers composition over inheritance, may be this is a viable approach in some plain JS patterns but this is not the case.
As you are new to React, take a look at https://reactjs.org/docs/composition-vs-inheritance.html
"At Facebook, we use React in thousands of components, and we haven’t found any use cases where we would recommend creating component inheritance hierarchies."
I tried to cast the anonymous object to Readonly<S> and the error went away.
this.state = { shouldHibernate: props.isHibernatory } as Readonly<S>;

Higher Order Function and Classes in different files

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;
}
}

Resources