React class change in a extend the generic State/Prop - reactjs

Basically I have a ViewMaster with many many Functions that gets somewhere in a wrapper => components executed
Now I want to have a different but mostly the same View that needs some extra Functions. Now 2 states are changing its type from
interface ViewState {
something:something
...
}
to
interface NewViewState extends ViewState {
change:change
}
But how am I able to do this.
My ViewMaster looks like this
class ViewMaster extends React.Component<ViewProps,ViewState>{}
and my new View
class ViewNew extends ViewMaster
But how am I able to set a new ViewState generic?
EDIT: Thinking about it, I can simply change the interface ViewState
to
interface ViewState {
change:change|something
}
But still It would be intresting to know

There are a number of ways you can do this. Given that your base component is a React Component and it already has parameterization I would do something like this:
class ViewMaster<P = ViewProps, S = ViewState> extends React.Component<P, S>{}
and then
class ViewNew extends ViewMaster<ViewProps, NewViewState>{}

Related

Using class member as a type

I want to tell Typescript that one member of one of my class is coming from another class.
For example:
Class Main{
getDataFromChild(data){
console.log(data)
}
}
Class Child{
getDataFromChild: Main.getDataFromChild //<== something like this
}
Use Case
My use case is React parent passing method down to React child. I want my IDE to navigate to Parent method decleration when I click on the passed method inside child.
export default class Parent extends Component {
simplifiedFunction (value) { // Line A
console.log(value)
}
render () {
return (
<div>
<Child
simplifiedFunction = {this.simplifiedFunction}
/>
</div>
)
}
}
export default class Child extends Component {
render () {
return (
<div>
<h1 onClick= { () =>
this.props.simplifiedFunction(<SomethingThatYouWantToPassIn>)//<== ctrl + click on simplifiedFunction should take me to its definition in parent class (Line A)
}
> Something</h1>
</div>
)
}
}
You could define an interface (named or anonymous):
interface SharedFeature {
sharedFunction: () => void;
}
and then implement it on the parent
class Parent extends React.Component implements SharedFeature {
sharedFunction() { ... }
render() { return <Child sharedFunction={sharedFunction} />; }
}
finally you can use the interface as part of the Child component's props
class Child extends React.Component<SharedFeature, {}> {
render() {
return <div>
{this.props.sharedFunction()}
</div>;
}
}
When using Component class you can take advantage of it's two generic arguments, first one defined the interface of props and the second one defines interface of the state. You can either use SharedFeature interface directly or extend another interface by it and then use that interface for props.
I dont know what kind of IDE you use but usually in this case when you do "Go to implementation" the IDE should succesfuly locate class Parent as it implements the interface that defines the function sharedFunction();
Beware that if you do "Go to definition" you WILL be taken to the declaration of the interface SharedFeature. If you want to use "Go to definition" nontheless (antipattern)use the class Parent itself as type for class Child's props. However this also makes Parent's method render() and ANY OTHER MEMBERS part of the props aswell.
I don't think this will work the way you are hoping it will. Namely your <Child/> component shouldn't have any awareness as to what passed it the simplifiedFunction prop. In your case, you happen to be passing it in from <Parent/>, but you should be able to pass in any function that satisfies the contract of the function. Imagine you had 3 other components that passed in different functions, your child component should never have to care about that.
Your question says that you "want to tell Typescript that one member of one of my class is coming from another class." This is an XY problem, in the sense that you say you want to do one thing (copy a method from one class to another) but your goal is something else (passing a method to a React component), and this should be done a different way.
Luk's answer gives a good solution for your actual use case. In my answer I'll address the original question about copying a method from one class to another, and explain why doing that is not a good solution.
Strictly speaking, the answer is simple:
class Main {
foo(): void {
console.log('bar');
}
}
class Child {
foo = Main.prototype.foo;
}
However, do not do this. You will be arrested by the OOP police, and sent to OOP jail.
It almost always doesn't make sense to do this. A method on the class Main may make use of properties that are defined in that class. Child is not a subclass of Main so it does not necessarily have the same properties. What if it's like this?
class Main {
x: string = 'bar';
foo(): void {
console.log(this.x);
}
}
class Child {
foo = Main.prototype.foo;
}
Now what is new Child().foo() supposed to do? A Child has no x property to log.
If the foo method really doesn't access any of Main's properties, and this fact is part of Main's contract (as depended on in the Child class), then the method should be static and the Child class should invoke it as Main.foo().
If the foo method does access some of Main's properties, but only properties shared by the Child class, then you should design your class hierarchy so that either Child is a subclass of Main, or so that those properties and the foo method belong to a common superclass, so that they can be shared by inheritance.

Binding the List items to this.state

I have a Recact component defined as:
export default class App extends React.Component<AppProps, Items>
The Items class is defined as:
export default class Items extends Array<Item>
and the Item is just a class with a bunch of properties.
If in render() I bind a list to my Items object directly ie.
<List items={myItems} onRenderCell={this._onRenderCell} />
the list is displayed correctly, however if I this.setState(myItems) and then try to get the list binded to the state:
<List items={this.state} onRenderCell={this._onRenderCell} />
I get the compile-time error:
Type 'Readonly' is missing the following properties from type 'any[]': [Symbol.iterator], [Symbol.unscopables]
How do I fix this?
I found an easy solution to this... I created a new type:
type ItemsState = {
items: Items
}
then changed my component to use that as a state instead: export default class App extends React.Component<AppProps, ItemsState>
First, I want to point out that React's patterns discourage inheritance and prefer composition
Moreover you are extending an Array while React expect an Object.
Another misconception is "bind a function to the state". You are binding this context, not to variables.
Finally, state must be serializable and you should put in it only objects and arrays or primitives.
Try to refactor your components following this guidelines or post complete code for a more comprehensive solution.

Why can't you use the types of TypeScript abstract classes to create React factories, when non-abstract classes work fine?

The following Typescript code using React compiles and works just fine:
import * as React from "react";
class MyComponent<P> extends React.Component<P, any> {
static getFactory() {
return React.createFactory(this);
}
}
However, the following code, similar in every way except the class is now abstract, throws an error at compilation, on the line where the factory is created:
import * as React from "react";
abstract class MyComponent<P> extends React.Component<P, any> {
static getFactory() {
return React.createFactory(this);
}
}
The error is:
error TS2345: Build:Argument of type 'typeof MyComponent' is not assignable to parameter of type 'ComponentClass<any> | StatelessComponent<any>'.
I can think of no reason why it should make a difference whether the class is marked as abstract or not.
Any insights as to why this might be the case?
================ADDED 9/22/2016=============
In my example above, I probably should have called MyComponent something like MyBaseComponent. The whole point here is that I am creating a framework component that I never want instantiated. I only want things derived from MyComponent to ever be instantiated. (Note, for example, that MyComponent doesn't even have a render() method declared.)
So.... I declare a really-to-be-instantiated class as:
interface MyDerivedComponentProps {
id: string;
}
class MyDerivedComponent extends MyComponent<MyDerivedComponentProps> {
render() {
return /* Some JSX rendering stuff here */
}
}
Note that this all works just fine.... if I don't ever instantiate MyComponent but do instantiate MyDerivedComponent. But the simple act of decorating MyComponent with the abstract keyword, which ought to serve to ensure that MyComponent will never be directly instantiated causes this compiler error. And I'm still of the opinion that it shouldn't. The compiler is handling the inheritance just fine if the base class is not marked as abstract. It should also handle it fine if the base class is marked as abstract, or perhaps not, but if not, why?
================ADDED 9/29/2016=============
As per request in comments, here is a complete example code showing both declarations and inheritance for both:
import * as React from "react";
import * as ReactDOM from "react-dom";
interface MyDerivedComponentProps {
id: string;
}
// WORKING EXAMPLE
class MyBaseNonAbstractComponent<P> extends React.Component<P, any> {
static getFactory() {
return React.createFactory(this); // NO COMPILER ERROR HERE
}
}
class MyDerivedFromNonAbstractComponent extends MyBaseNonAbstractComponent<MyDerivedComponentProps> {
render() {
return <div>Hello from id {this.props.id}</div>;
}
}
// NOT WORKING EXAMPLE
abstract class MyBaseAbstractComponent<P> extends React.Component<P, any> {
static getFactory() {
return React.createFactory(this); // HERE IS THE COMPILER ERROR
}
}
class MyDerivedFromAbstractComponent extends MyBaseAbstractComponent<MyDerivedComponentProps> {
render() {
return <div>Hello from id {this.props.id}</div>;
}
}
ReactDOM.render(MyDerivedFromNonAbstractComponent.getFactory()({ id: "idForNonAbstract" }), document.getElementById("managed-content-for-non-abstract"));
ReactDOM.render(MyDerivedFromAbstractComponent.getFactory()({ id: "idForAbstract" }), document.getElementById("managed-content-for-abstract"));
================ADDED 9/29/2016 #2=============
It is definitely the case that whether or not a base class is marked abstract, if it has a static method on it, and that method refers to this, then it will actually dereference to the constructor of the derived class used to invoke the method.
So, if we have the following:
class BaseConcrete {
static logWhoAmI() {
console.log(`this is ${this}`);
}
}
class DerivedConcrete extends BaseConcrete {
}
DerivedConcrete.logWhoAmI(); // This will log constructor of DerivedConcrete
abstract class BaseAbstract {
static logWhoAmI() {
console.log(`this is ${this}`);
}
}
class DerivedAbstract extends BaseAbstract {
}
DerivedAbstract.logWhoAmI(); // This will log constructor of DerivedAbstract
So it appears that Typescript does not treat this referred to in a static method of a base class any differently when that base class is abstract or not.
It is only, somehow, the signature of the React.createFactory() method that seems to get confused somehow when passed the "this". It complains because on the face of it, it seems as if it is being passed a constructor for an abstract method, which would of course be illegal. But since the class itself is abstract, the this at runtime is guaranteed to be a derived class, and React.createFactory should work just fine. In fact, as one commenter pointed out, if you cast the argument to React.createFactory to any, it will work fine at runtime. But this is hack it seems to me. Appreciate the discussion this far though.
Abstract classes cannot be directly instantiated with new (that's why they are called so). Thus, you can't use them as React components. They can be the base classes for other components, though.
And that's the thing TypeScript compilers checks statically. Since abstract class doesn't have new operator, it cannot be coerced to ComponentClass.

In componentDidUpdate refs is undefined

I want to use Chart.js on my website. As you can see title, I'm using React.js. To use Chart.js, I need the canvas and context like this:
let context = document.getElementById('canvas').getContext('2d');
let chart = new Chart(context, ...);
so I design the component like this:
export function updateChart() {
let context = this.refs.chart.getContext('2d');
let chart = new Chart(context ,... );
...
}
export default class GraphChart extends React.Component {
constructor() {
super();
updateChart = updateChart.bind(this);
}
componentDidMount() {
updateChart();
}
render() {
return <canvas ref="chart" className="chart"></canvas>;
}
}
as you can see, I exported two things, update chart function and GraphChart class. Both will using in parent component like this:
import { updateChart } from './GraphChart';
import GraphChart from './GraphChart';
class Graph extends React.Component {
...
someKindOfAction() {
// update chart from here!
updateChart();
}
render() {
return (
<div>
<SomeOtherComponents />
<GraphChart />
</div>
);
}
}
then Parent class using exported updateChart function to update chart directly. It was working, but only first time. After unmount and mount the GraphChart component, it's refs are just empty.
Why refs is empty? And If I did wrong way, how can I get canvas context for initialize Chart.js?
Object refs is undefined, because this is not what you think it is. Try logging it.
The function you’re exporting is not bound to this of your component. Or perhaps it is, but to the last created instance of your component. You can never be sure that’s the mounted instance. And even if you are, you can not use multiple instances at the same time. So, I would dismiss this approach entirely.
Other than that, providing the function to alter some component’s state is exactly the opposite of what’s React is trying to accomplish. The very basic idea is that the component should know to render itself given some properties.
The problem you are trying to solve lies in the nature of Canvas API, which is procedural. Your goal is to bridge the gap between declarative (React) and procedural (Canvas) code.
There are some libraries which do exactly that. Have you tried react-chartjs? https://github.com/reactjs/react-chartjs
Anyways, if you’re wondering how the hell should you implement it the “React way”, the key is to declare properties your component handles (not necessarily, but preferably), and then to use component lifecycle methods (e.g. componentWillReceiveProps and others) to detect when properties change and act accordingly (perform changes to the canvas).
Hope this helps! Good luck!

React: call parent version of the functiion when overriding

When I'm overriding a function in a child class, how can I call the parent version of that function?
Say I have two components AbstractList and SortableList in React with:
class AbstractList extends React.Component {
getItems() {
...
}
}
class SortableList extends AbstractList {
getItems() {
... do sorting first
return super.getItems(); // QUESTION: how am I supported to actually do this?
}
}
Thanks!
You should not do it this way
check this issue on github
Many people have become accustomed to using OO inheritance not just as
a tool, but as the primary means of abstraction in their application.
I've you've worked at a Java shop, you'll know what I'm talking about.
In my personal opinion, classical OO inheritance (as implemented in
many popular languages) is not often the best tool for most jobs, let
alone all jobs. But the situation should be approached with even more
caution when inheritance is used within a framework or paradigm that
uses functional composition as its primary abstraction (React). There
are certain patterns we'll want to prevent (there are many strange
things that people can come up with when combining render with
inheritance that don't make sense and are addressed via simple
composition). There's also risk of making mutation more convenient. It
might make sense to start with ES6 classes simply as a better syntax
for React component creation, intentionally limiting some use cases
(limiting inheritance depth, making some React base class methods
final) (only when used with React components of course - all non-React
use of ES6 classes wouldn't be restricted).
Instead of this
class AbstractList extends React.Component {
getItems() {
...
}
}
class SortableList extends AbstractList {
getItems() {
... do sorting first
return super.getItems();
}
}
You should do
class AbstractList extends React.Component {
getItems() {
...
}
}
class SortableList extends React.Component {
render() {
return <AbstractList items={this.props.item}>{this.props.children}</AbstractList>;
}
}

Resources