Noob
Noob

Reputation: 1014

How to correctly type render props in Flow?

I can't figure out how to correctly type a Wrapper and Counter component when using render props. I've tried the following but it just leads to more errors:

App.js

// @flow
const App = (): React.Node => {

  return (
    <div className="App">
      <Wrapper render={(count, increment) => {
        return <Counter1 count={count} increment={increment}/>
      }}/>

      <Wrapper render={(count, increment) => {
        return <Counter2 count={count} increment={increment} />
      }}/>
    </div>
  );
};

Wrapper.js

// @flow
import * as React from 'react';

type WrapperProps<T> = {
  render: T => React.Node
};

export const Wrapper = <T>({render}): Props<T> => {
  const [count, setCount] = React.useState<number>(0);

  const increment = (prevCount: number) => setCount(count + 1);

  return (
    <div>
      {render(count, increment)}
    </div>
  );
}

Counter1.js

// @flow
import * as React from 'react';

export const Counter1: React.Node = ({ count, increment }) => {
  return (
    <>
      <div>Counter 1: {count}</div>
      <button onClick={increment}>Increment</button>
    </>
  );
};

Counter2.js

// @flow
import * as React from 'react';

export const Counter2 = ({ count, increment }) => {
  return (
    <>
      <div>Counter 2: {count}</div>
      <button onClick={increment}>Increment</button>
    </>
  );
};

I currently have 14 errors and I'm really lost. I can't figure out how to type Wrapper props and counter props. Here are some of the errors:

Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because $Iterable [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because React.Element [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because React.Portal [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because boolean [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because null [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/App.js:35:17

Cannot create Counter1 element because number [1] is not a React component. [not-a-component]

     src/App.js
     32│         </div>
     33│       ))}
     34│       <Wrapper render={(count, increment) => {
     35│         return <Counter1 count={count} increment={increment}/>
     36│       }}/>
     37│
     38│       <Wrapper render={(count, increment) => {

     src/components/Counter1.js
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/components/Counter1.js:4:37

Cannot assign function to Counter1 because: [incompatible-type]
 • Either inexact function [1] is incompatible with exact object type [2].
 • Or function [1] is incompatible with React.Portal [3].
 • Or property @@iterator is missing in function [1] but exists in $Iterable [4].

     src/components/Counter1.js
      1│ // @flow
      2│ import * as React from 'react';
      3│
 [1]  4│ export const Counter1: React.Node = ({ count, increment }) => {
      5│   return (
      6│     <>
      7│       <div>Counter 1: {count}</div>
      8│       <button onClick={increment}>Increment</button>
      9│     </>
     10│   );
     11│ };
     12│

     /private/tmp/flow/flowlib_1fa18dde633e97c7_501/react.js
 [2] 18│   | React$Element<any>
 [3] 19│   | React$Portal
 [4] 20│   | Iterable<?React$Node>;


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/components/Counter2.js:4:26

Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type
annotation at array pattern: [signature-verification-failure]

     1│ // @flow
     2│ import * as React from 'react';
     3│
     4│ export const Counter2 = ({ count, increment }) => {
     5│   return (
     6│     <>
     7│       <div>Counter 2: {count}</div>


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/components/Counter2.js:4:47

Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type
annotation at function return: [signature-verification-failure]

     1│ // @flow
     2│ import * as React from 'react';
     3│
     4│ export const Counter2 = ({ count, increment }) => {
     5│   return (
     6│     <>
     7│       <div>Counter 2: {count}</div>


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/components/Wrapper.js:8:28

Cannot build a typed interface for this module. You should annotate the exports of this module with types. Missing type
annotation at array pattern: [signature-verification-failure]

      5│   render: T => React.Node
      6│ };
      7│
      8│ export const Wrapper = <T>({render}): Props<T> => {
      9│   const [count, setCount] = React.useState<number>(0);
     10│
     11│   const increment = (prevCount: number) => setCount(count + 1);


Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/components/Wrapper.js:8:39

Cannot resolve name Props. [cannot-resolve-name]

      5│   render: T => React.Node
      6│ };
      7│
      8│ export const Wrapper = <T>({render}): Props<T> => {
      9│   const [count, setCount] = React.useState<number>(0);
     10│
     11│   const increment = (prevCount: number) => setCount(count + 1);

Upvotes: 0

Views: 234

Answers (1)

Brianzchen
Brianzchen

Reputation: 1335

Your first issue is related to the Counter1 component line

export const Counter1: React.Node = ({ count, increment }) => {

Which basically is typing Counter1 as a React.Node, what you really want is a function that RETURNS a React.Node like so

export const Counter1 = ({ count, increment }): React.Node => {

Your second issue related to Counter2 is related to types-first where every module export has to be explicitly typed so that flow can run at maximum performance, you can read more about it here.


And your last issue in the Wrapper component is related to the fact that you have a type called WrapperProps but you've used it as Props so it's saying it can't find it. Also I believe you've placed your WrappedProps in the wrong place, so change it from

export const Wrapper = <T>({render}): Props<T> => {

to

export const Wrapper = <T>({render}: Props<T>): React.Node => {

Though in retrospect you might want to think about how a parent component will actually consume this and pass in the generic, because your render func is already expecting two arguments so a generic is not very helpful here. Based on your code, you probably want something like this

type WrapperProps = {
  render: (
    number,
    (number) => void,
  ) => React.Node,
};

export const Wrapper = ({render}: WrapperProps): React.Node => {

Upvotes: 1

Related Questions