Using class member as a type - reactjs

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.

Related

redefine componentWillReceiveProps in external component

I have a module:
import {ReferenceInputController} from 'ra-core'
I need to redefine it's componentWillReceiveProps, but I don't want to copy entire component and make it custom. Is there a more easy way to redefine it? Something like creating a new component which extends the ReferenceInputController etc ... ?
Assuming ReferenceInputController extends React.Component, then you should be able to achieve this using regular class inheritance.
To illustrate this, consider the following Foo class.
Here you can see that it extends the base class ReferenceInputController, while also providing custom logic for componentWillReceiveProps. This custom logic will be used in place of the equivalent componentWillReceiveProps method that was defined in ReferenceInputController:
class Foo extends ReferenceInputController {
/*
Redefinition of componentWillReceiveProps logic for Foo extension
of ReferenceInputController base class goes here
*/
componentWillReceiveProps(nextProps) {
console.log('Custom logic for Foo that is run during componentWillReceiveProps');
}
/*
The render method defined in your ReferenceInputController base class, as well as
any other component lifecycle methods defined in ReferenceInputController will be
invoked when Foo is rendered
render() {
...
}
*/
}
You would then use (or render) Foo just as you would use ReferenceInputController:
<div>
<Foo ... />
</div>
Here is a jsFiddle if you'd like to see this in action

Best way to pass store to a child of connected component in React-Redux

What's the correct way to implement the below design?
<Parent with state>
<connected element with Store configuration dependant on parent>
<child of both, dependant on store/connected element>
</connected element>
</parent>
I'm not sure how much code to include and there's almost certainly more than I need so here's a snippet that I think explains what I'm trying to accomplish.
class SceneOne extends React.Component {
constructor (props) {
super(props);
this.state = {
opacity: 0,
script: sceneOneScript
};
}
render () {
return (
<ScriptReader script = {this.state.script}> //This is connected and creates a store from the script passed via state.
<Screen data-image="caves.png" data-opacity={this.state.opacity} >//This uses actual SceneOne.state.opacity which is updated to 1 after a delay in ComponentDidMount
<ConditionalTitle props = {this.props}/> //This needs the store.
</Screen>
</ScriptReader>
);
}
}
I'm really hoping that I don't need to connect <ConditionalTitle> because that feels like it breaks agnostic components principles. I'm also hoping that I don't need to install <ConditionalTitle> inside the definition of <ScriptReader> because I'm planning on reusing it and passing different children/scripts etc.
ie. there'll be a that has a <ScreenReader> child and it may not have a title, or may have elements that aren't required in <SceneOne>.
use a HOC inside the definition of the ScriptReader that composes all of the options

In Typescript, is there any way to typecheck passed-in JSX.Element's?

Consider this simple case:
Client of library:
class ClientComponent extends React.Component<any,any> {
render() {
const myNestedElement = (<LibNestedComponent/>)
return <LibComponent nested={myNestedElement}/>
}
}
Library:
class LibNestedComponent extends React.Component<any,any> {
render() { return <div>nested stuff</div> }
}
interface LibComponentProps { nested: JSX.Element }
class LibComponent extends React.Component<LibComponentProps,any> {
render() {
return <div>{this.props.nested}</div>
}
}
As the author of Lib, I'd like to be able to tell my LibComponent clients, via the LibComponentProps interface, that the passed-in nested prop must be an element of type LibNestedComponent - not just any old j.random element. But AFAICT, there's no way to do this; the Typescript doc even says:
The JSX result type. By default the result of a JSX expression is typed as any. You can customize the type by specifying the JSX.Element
interface. However, it is not possible to retrieve type information
about the element, attributes or children of the JSX from this
interface. It is a black box.
Does anyone have a workaround that achieves this kind of typechecking without too much pain?
(The example is deliberately trivial and is not meant to be a sensible use-case.)
Apart from my comment, I don't know if there is a typescript way to restrict the type of component that client component can pass. But there is a way through which you can determine the type of component that was passed as a prop.
You can check the name of the component which was passed and see which type is it.
if (this.props.nested.type.name === 'LibNestedComponent') {
console.log('A valid component is passed');
} else {
console.log('Invalid component is passed');
}
where nested is the component you passed in the example you provided.
In the below picture you can see the name of the component.
But again, this would be a run-time detection.
You can check the type of this.props.nested using the object
if (this.props.nested.type !== LibNestedComponent) {
throw new Error('Invalid prop passed. Make sure that an instance of LibNestedComponent is passed through the nested prop')
}
The problem with Hardik's answer is that, in case your code gets minified, the value of type.name will change and your code will fail. So you should go for the type property directly instead.

In React how do I have my child tell the parent its key?

In React we are loading a list of children components that define their own ID. We want to then do a pass where we re-arrange the children based on their set internal ID. How do we communicate the internal ID to the parent? Also it would be nice to use that ID as the parent rendering ekey. Note the internal ID does not match the component name.
class Thing1 extends React.Component {
const ID = 'Thing1IDString';
}
class Thing2 extends React.Component {
const ID = 'Thing2IDString';
}
<Parent>
<Thing1 />
<Thing2 />
<Thing3 />
</Parent>
The first thing I'd suggest is trying to make the parent know how to compute the child IDs somehow. If the child IDs come from a database, then make the parent compute them instead of making the children compute them. The lower components in the tree should be less intelligent. Hand them everything they need to know in its final form, so they can simply render it.
The structure you're talking about, where the child has "private" data that the parent needs to act upon, is generally solved in React by:
Storing that data in the parent
Passing the data to the child as a prop
Passing an onDataChanged function to the child, so that the child can tell the parent when the data changes.
This is the "controlled input" pattern. It feels awkward at first, and may seem like there's too much indirection at first glance. But the advantage is that the data flow is very predictable.
Now, if that isn't possible in your case, and you really need the children to "register" with the parent, then you can use props or context to pass a registerChild function to the children. In the child, use its componentDidMount lifecycle method to call the registerChild function with its computed ID. The parent can then keep track of these IDs however it needs to.
Your syntax is kind of confusing. Let's convert the pseudo-example to an actual working ReactJS code sample.
Let's say you have a child(s) component(s):
class Thing1 extends React.Component {
constructor(props) {
super(props);
this.id = 'Thing1IDString';
}
render(){
return (
<p>Child component</p>
);
}
}
If you want to access Thing1's id property from your parent component, there are several ways how to do communicate between components.
It depends what you want to achieve. If you just want to access a child component property, you may use refs:
class Parent extends React.Component {
doSomething() {
// Access anything you need from `this.refs.thing1`
const thing1ID = this.refs.thing1.id;
}
render() {
return (
<div>
<button onClick={this.doSomething.bind(this)}>Get ID</button>
<Thing1 ref="thing1" />
</p>
);
}
}
I've touched above problem here: https://github.com/facebook/react/issues/15320
There are 3 methods you can use to achieve Parent child data transfer (context Api, mutatiin, Reac.Children deep traversal).

How to access data in a child react component from a parent react component

I'm new to ReactJS. I want to be able to set some properties of a React component and then be able to access it from a parent React component. But I'm not entirely sure how to do this. For example, consider the following two classes:
export default class SubWindow extends React.Component {
click(event)
{
this.myCollection.push({name:'receiptNum',value:$(event.currentTarget).html()});
}
render()
{
return (
<ul>
<li onClick={this.click.bind(this)}>0</li>
<li onClick={this.click.bind(this)}>1</li>
<li onClick={this.click.bind(this)}>2</li>
<li onClick={this.click.bind(this)}>3</li>
</ul>
);
}
}
export default class MainWindow extends React.Component {
click(event)
{
console.log(SubWindow.myCollection);
}
render()
{
const SubWindow = require('./SubWindow').default;
return (
<SubWindow />
<button onClick={this.click}>Log subwindow array</button>
);
}
}
Basically, I want the SubWindow to have a property called myCollection which is just an array of JSON objects. myCollection gets populated by each click on the list item.
Later, I want to be able to console.log(SubWindow.myCollection) when I press on a button in the parent window. My question how do I access the SubWindow.myCollection from a parent react component?
I would recommend you to solve this problem by using callback. MainWindow is creating SubWindow, and you can give here a callback function as a property. For example:
<SubWindow onClick={this.onSubwindowClick} />
Now in your SubWindow class, just call this callback inside your click function:
click(event)
{
this.myCollection.push({name:'receiptNum',value:$(event.currentTarget).html()});
this.props.onClick(/* data you want to pass to the parent */);
}
Also you have to define onSubwindowClick in the class MainWindow. This function should receive any data you wish from child class - the data which you pass from child where I put comment /* data you want to pass to the parent */.
Also, don't forget to bind this to that onSubwindowClick function. This is usually done in constructor of the class, which I suggest you to create for each component.
You can find good example about "passing data to parent" on React's pages. You can take a look at the article Thinking in React, particularly section "Step 5: Add inverse data flow".
just pass a function to you child component, and the function is bind to the parent component's 'this', actually you just created a closure.
then in your parent component, the function's args are passed in your child component, meanwhile your parent component's scope has access to the args, so in the parent scope you can get access to the data in the child scope.

Resources