The Flow Type cookbook

Inheritance from a common ancestor

You will often need to declare types that mirror your backend structure, in which you probably have one base (and often abstract) class and different children. It's easy to have an object type "extend" another by using the intersection operator & :

type B = A & {
  value: string,
};

However, we sometimes have methods that need to work with either the base class, or any of the inherited types. In that case, we declared three Flow types to be used when typing the method's arguments and return values :

  • A base type that contains all the common elements:

    type BaseDocument = {
      id: string,
      name: string,
    };
    
  • Several children types :

    type TextDocument = BaseDocument & {
      content: string,
    };
    
    type File = {
      path: string,
      size: number,
      hash: string,
    }
    
    type FileDocument = BaseDocument & {
      value: File,
    };
    
  • A type that regroups all the children types:

    type Document = TextDocument | FileDocument;
    

React components with imperative behaviour

Background

Oftentimes, especially if you want to use refs and children, you will need to use React's type definitions like Component, Element and Class. We strongly suggest that you read about those to understand their differences, and specifically this page of the React documentation.

If we need to work with components that have an imperative behaviour (i.e their parent components will call methods from the children instances) the usual workflow is to use a ref to store a reference to the children, and then to call a method on the child using that reference. We could even imagine a case where we are given the child component's class as a prop, but the instantiation is left to us.

At first, we would think we may use Component. When a component is defined as a pure function, Flow doesn't recognize it as a Component type, but it does see it as a ReactClass. Taking this into account, our props would receive a ReactClass when the component hasn't been instantiated yet. The proper syntax is :

type Props: {
  imperativeChild: ReactClass<*>,
};

Then, after having instantiated it, we are able to access it using a ref. String refs are legacy, and should not be used anymore (source), hence the need for a callback method.
The argument of the callback are not defined as of now (ref: any; in the source code, but Facebook developers want to improve it). Anyway, since it gives us a reference to the instance, it makes sense to use React.Element. Here is an example of the ref initialization method :

initChildComponentRef = (panel: React.Element<*>) => {
  this.panel = panel;
}

The complete implementation

Say we have a to panels layout that receives one of its panel as a prop. It doesn't know what this component will be, but it knows this panel has a reset method.

class PanelLayout extends React.Component {
  props: {
    panelComponent: ReactClass<*>, // React.Class, because the panel's class is given as prop
  }
  panel: React.Element<*>;

  initPanelRef = (panel: React.Element<*>) => { // React.Element, because it is a mounted instance
    this.panel = panel;
  }

  reset = () => {
    if (typeof this.panel.reset !== 'function') return; // guard for flow, to prevent failing in case the panel doesn't have a reset method
    this.panel.reset();
  }

  render() {
    const PanelComponent = this.props.panelComponent; // uppercase first letter is mandatory for React
    return (
      <div>
        <div>
          {/* This button's purpose is to reset the panel : */}
          <button onClick={this.reset}>Reset panel</button>
        </div>
        <PanelComponent ref={this.initPanelRef} />
      </div>
    );
  }
}

results matching ""

    No results matching ""