How to describe this.props and this.store? - reactjs

I inherit from the react component of Cell. But the linter swears: The "props" property does not exist in the "Creative" type. The same is true for this.state and this.setState.
export default class Creative extends Cell<Props> {
constructor(props) {
super(props);
}
render() {
const { rowData: { creative }, rowNumber } = this.props;
How to fix it?

The Creative class inherits Cell so you have to add to the Creative class and in its props extend from the other props of the other component.
interface CreativeProps extends CellProps { //Its own props}
interface CreativeState { //Its state }
export default class Creative extends React.Component<CreativeProps , CreativeState> {
constructor(props) {
super(props);
}
render() {
const { rowData: { creative }, rowNumber } = this.props;
}
However, these are already implicit in Creative as long as you have declared them in Cell. The above (//...) is if you'd like to add props and state extra
You have an example here: https://codesandbox.io/s/react-typescript-tujz6

Related

Typing a class component with no props

I am trying to type a class component with no props
import React, { Component } from "react";
type Props = {};
class MyComponent extends Component<Props> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
render() {
return <h1>hello</h1>;
}
}
export default MyComponent;
It seems like I just cannot ommit them
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor() {
super();
this.state = { hasError: false };
}
What is the correct way to type a class component with no props?
You can initialize the state like this, avoiding to (manually) overwrite the constructor.
class ErrorBoundary extends Component {
state = {
hasError: false
}
render() {
return <h1>hello</h1>;
}
}
I think that's what you're talking about, not the typings.

Cannot read property 'checked' of undefined, from React.Component static funtion

I have a React.Component and i want to call this static function from the many different React.Component.
class Categories extends React.Component {
constructor(props) {
super(props);
this.getCheckedCategories = this.getCheckedCategories.bind(this);
this.state = {
checked: [],
unchecked: []
};
}
static getCheckedCategories() {
return this.state.checked;
}
}
So I tried to connect the function.
import Categories from './product/checkboxes';
class FullWidthGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
const { classes } = this.props;
const checkedCategories = Categories.getCheckedCategories();
}
}
static functions can't access this, i.e. static method calls are made directly on the class and are not callable on instances of the class. You can read more on that here.
So in your case you could do:
class Categories extends Component {
state = {
checked: [],
unchecked: []
};
static getCheckedCategories = (klass) => {
return klass.state.checked
}
render() {
return (
<div>{Categories.getCheckedCategories(this)}</div>
);
}
}
Working example here.
That's the purpose of static function. It cannot access the instance (this).
You can have multiple instances of class but only one static function/property.
As a workaround (depend on what you want to do), you can use static property to store your state:
class Categories extends React.Component {
constructor(props) {
super(props);
Categories.state = {
checked: [],
unchecked: []
};
}
static getCheckedCategories() {
return Categories.state.checked;
}
}
However it won't work with setState since it is an instance method.
Imagine situation when you have muptiple Categories components and each one has different checked/unchecked categories. What would then Categories.getCheckedCategories() function returns?
If you want to have shared state (checked categories), I would recommend you to pull out the state out of the component. For example store it in parent component and pass it as props to child components. Or use state library like redux. You could also use react's context to manage shared state.

React/TypeScript: extending a component with additional properties

I am trying to use react to recreate my currents components (written in pure typescript) but I can't find a way to give additional props to a component extending an other.
export interface DataTableProps {
columns: any[];
data: any[];
}
export class DataTable extends React.Component<DataTableProps, {}> {
render() {
// -- I can use this.props.columns and this.props.data --
}
}
export class AnimalTable extends DataTable {
render() {
// -- I would need to use a this.props.onClickFunction --
}
}
My problem is that I need to give AnimalTable some props that would be irrelevant to DataTable. How can I do that ?
You'll need to make DataTable generic so that you'll be able to use an interface which extends DataTableProps:
export interface AnimalTableProps extends DataTableProps {
onClickFunction: Function;
}
export class DataTable<T extends DataTableProps> extends React.Component<T, {}> { }
export class AnimalTable extends DataTable<AnimalTableProps> {
render() {
// this.props.onClickFunction should be available
}
}
as a rule of thumb it is probably better to avoid inheritance. luckily TS and react are great tools allowing that (unlike c# for example, where inheritance often saves you a bunch of boilerplate)
export interface DataTableProps {
columns: any[];
data: any[];
}
export class DataTable extends React.Component<DataTableProps, {}> {
render() {
// -- I can use this.props.columns and this.props.data --
}
}
export type AnimalTableProps = DataTableProps & {
onClickFunction: () => void;
};
export class AnimalTable extends React.Component<AnimalTableProps, {}> {
render() {
const {onClickFunction, ...tableProps} = this.props;
// use onClickFunction however you need it
return <DataTable {...tableProps}></DataTable>
}
}
The most elegant solution that I found (without extra generic class) is
interface IBaseProps {
name: string;
}
class Base<P> extends React.Component<P & IBaseProps, {}>{
}
interface IChildProps extends IBaseProps {
id: number;
}
class Child extends Base<IChildProps> {
render(): JSX.Element {
return (
<div>
{this.props.id}
{this.props.name}
</div>
);
}
}
For those who need, base classes can declare required/abstract methods that all instances must implement:
import { Component } from 'react'
abstract class TestComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {
abstract test(): string
}
type Props = {
first: string,
last: string,
}
type State = {
fullName: string,
}
class MyTest extends TestComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
fullName: `${props.first} ${props.last}`
}
}
test() {
const { fullName } = this.state
return fullName
}
}
Complete example of creating a component that you can extend off of and maintain state and props
import { Component } from "react";
// Props for the Base Component
export interface BaseComponentProps { }
// State for the Base Component
export interface BaseComponentState {
isLoaded?: boolean
}
// The Base Component that your components can extend
export class BaseComponent<Props extends BaseComponentProps, State extends BaseComponentState, SS = any> extends Component<Props, State, SS> {
State: BaseComponentState = {
isLoaded: false
}
constructor(props: Props) {
super(props);
}
componentDidMount() {
this.setState({ isLoaded: true })
}
}
// Props for your specialized component
export interface MainComponentProps extends BaseComponentProps {
}
// State for your specialized component
export interface MainComponentState extends BaseComponentState {
CanRead: boolean
}
// Your component which now extends the BaseComponent
export class MainComponent extends BaseComponent<MainComponentProps, MainComponentState> {
state: MainComponentState = {
CanRead: false
}
componentDidMount() {
super.componentDidMount();
if (this.state.isLoaded) {
this.setState({ CanRead: true })
}
}
}

Is the call to super(props) in an ES6 class important?

Suppose I've the following class:
class Tabs extends React.Component {
displayName: Tabs;
static propTypes = {
selected: React.PropTypes.number,
children: React.PropTypes.oneOfType([
React.PropTypes.array,
React.PropTypes.element
]).isRequired
};
constructor() {
super();
this.state = {
selected: 0,
maxSelected: 0
};
render() {
return(
<div>
{this.props.selected}
{this.props.children}
</div>
);
}
};
I want to know that if passing the following constructor is important:
constructor(props) {
super(props);
}
My current code works just fine but I wanted to know if this is a good practice.
According to Sophie Alpert with the React team it's only necessary to pass props into the constructor if you intend on using this.props inside the constructor. After the constructor is invoked, React attaches the props to the component from the outside.

Warning: getInitialState was defined on DimensionPicker, a plain JavaScript class. This is only supported for classes created using React.createClass

I am trying to write my first react control. Here is what I have written
import React from 'react';
import DimensionPickerAction from '../actions/DimensionPickerActions.js';
import MovieLensAppStore from '../stores/MovieLensAppStore.js';
class DimensionPicker extends React.Component {
constructor(props) {
super(props);
this.state = { items: [], currentItem: '' };
}
getInitialState() {
this.state = {
items: MovieLensAppStore.getAttributes(this.props.dimension),
currentItem : MovieLensAppStore.getCurrentAttribute(this.props.dimension)
};
}
onSelectionChange(newValue) {
DimensionPickerAction.selectionChange(this.props.dimension, newValue);
}
render() {
var optionNodes = this.state.items.map((item) => {
if (item === this.state.currentItem)
return(<option value="{item}" selected>{item}</option>)
else
return(<option value="{item}">{item}</option>)
});
return(<div><select onchange="onSelectionChange">{optionNodes}</select></div>);
}
}
export default DimensionPicker;
Very surprisingly, I get an error
Warning: getInitialState was defined on DimensionPicker, a plain JavaScript
class. This is only supported for classes created using React.createClass. Did
you mean to define a state property instead?
I find this very confusing because clearly my component derives from React.Component
Eric's comment is correct. You're using ES6 classes, which means that getInitialState is not supported. You'll need to change this:
class DimensionPicker extends React.Component {
constructor(props) {
super(props);
this.state = { items: [], currentItem: '' };
}
getInitialState() {
this.state = {
items: MovieLensAppStore.getAttributes(this.props.dimension),
currentItem : MovieLensAppStore.getCurrentAttribute(this.props.dimension)
};
}
to this:
class DimensionPicker extends React.Component {
constructor(props) {
super(props);
this.state = {
items: MovieLensAppStore.getAttributes(props.dimension),
currentItem : MovieLensAppStore.getCurrentAttribute(props.dimension)
};
}
What about this, if you like to save the initial state construction somewhere for later use:
class DimensionPicker extends React.Component {
constructor(props) {
super(props);
this._getInitialState = this._getInitialState.bind(this)
this.state = this._getInitialState();
}
_getInitialState() {
return { items: [], currentItem: '' }
}

Resources