Sometimes React just doesn’t seem to let you do what you want. You want to have a parent call a function in a child when it wants to, and React, as wonderful is at is, just doesn’t seem to support that. Enter: Double Pointer.
This method should only be used if the other options are first ruled out. And as much as I will try to dissuade you from using this option, there are times where it is super useful. I like to call it the “Double Pointer” (okay, I borrowed the name from C++, if anyone finds there is an official name, please leave it in the comments). This can be used when you have a child with an instance method inside of it that you need to call for some purpose, but again, first try one of the above methods. Here is an example on Stack Overflow: Get selectedKey from dropdown office fabric ui. The main idea is this:
Child
Prop:
{ setGetDataFunction?: (getData: () => DataType ) => void }
Constructor:
this.props.setGetDataFunction && this.props.setGetDataFunction(this.getDataset);
Parent
getData: any;
setGetDataFunction = (getData: () => IExportData) => {
this.getData = getData;
}
render() {
<Child setGetDataFunction = this.setGetDataFunction/>
}
Now you have a reference to the getDataset in the child in your parent. This method is not the easiest to follow and not intuitive, but in the absence of being able to use one of the methods, can work great. This is a way to facilitate the event communication. The example above really should be refactored to use option #1 since its only purpose was to get data which the parent should already know about at all times (hey, it’s on my to-do list, at the time I didn’t know any better…). One example where this is a good fit is if one component has an action button, but another component has the action that needs to be called. Here is a fully working simplified example of a child component that has the state and what it does with the state, but the parent has the actual button (this as is would never be written like this, but if you think of a button that is injected in toolbar and a child component that controls everything else about it, except the button, it should make more sense):
import * as React from 'react';
export class Parent extends React.Component<any, any> {
incrementCounter: any;
setIncrementCounterFunction = (incrementCounter: () => number) => {
this.incrementCounter = incrementCounter;
}
onClick = () => {
this.incrementCounter && this.incrementCounter();
}
render() {
return <>
<button onClick={this.onClick}>Click Me</button>
<Child setIncrementCounterFunction={this.setIncrementCounterFunction} />
</>
};
}
interface ChildProps {
setIncrementCounterFunction?: (incrementCounter: () => number) => void;
}
class Child extends React.Component<ChildProps, { clickCount: number }>{
constructor(props: ChildProps) {
super(props);
this.state = { clickCount: 0 };
this.props.setIncrementCounterFunction && this.props.setIncrementCounterFunction(this.incrementCounter);
}
incrementCounter = () => {
this.setState({ clickCount: this.state.clickCount + 1 });
return this.state.clickCount;
}
render() {
return <span style={{ paddingLeft: "1em" }}>{`Clicked ${this.state.clickCount}`}</span>
}
}
The end result is a button you can click and a span with the clicked count. Here is a working copy of this code: React Typescript Playground. Basically, in JS terms, what you are doing is passing a callback to the child via the props that takes a callback as a parameter for the real function we want. The child then sends up the actual callback we want to the parent via the passed in callback. It feels like to me assigning a pointer to a pointer in C++, a Double Pointer.