Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Stateless Functional React components #1081

Closed
AsaAyers opened this issue Nov 11, 2015 · 32 comments
Closed

Support Stateless Functional React components #1081

AsaAyers opened this issue Nov 11, 2015 · 32 comments
Assignees

Comments

@AsaAyers
Copy link

Here is an ES6 class component:

import React from 'react'
type Props = {
    foo: number,
};

class ExampleClass extends React.Component<{}, Props, {}> {
    render() {
        return <div>{this.props.foo}</div>
    }
}

const testClass = <ExampleClass foo="string" /> // error

This works as expected, but how do you annotate a stateless functional component? This is the best I could do, but it doesn't produce an error and it should.

import React from 'react'
type Props = {
    foo: number,
};

function ExampleFunction({foo}: Props): ReactElement {
    return <div>{foo}</div>
}

const testFunction = <ExampleFunction foo="string" /> // no error

edit: Renamed the examples so the names don't conflict.

@AsaAyers
Copy link
Author

I dug into this as far as I could, and here is what I could find:

Type of a React element. React elements are commonly created using JSX
literals, which desugar to React.createElement calls (see below).
source

So this leads me to createElement

  declare function createElement<DefaultProps, Props, State, A: $Diff<Props, DefaultProps>>(
    name: ReactClass<DefaultProps, Props, State>,
    attributes: A,
    children?: any
  ): ReactElement<DefaultProps, Props, State>;

So Flow is aware that <ExampleClass foo="string" /> will desugar to React.createElement(ExampleClass, { foo: "string" }) and it type checks that. When using a functional component the type isn't ReactClass<DefaultProps, Props, State>. But why doesn't Flow complain that I just passed it a Function and not a ReactClass<DefaultProps, Props, State>?

// This works as expected
const testClass2 = React.createElement(ExampleClass, { foo: 1 })

// this one complains that the first parameter is the wrong type:
// ========================
// call of method `createElement`
// string. This type is incompatible with
// class type: existential. See: /tmp/flow/flowlib_e5f5399/react.js:132
const testString = React.createElement("Hello", { foo: 1 })

// No error here even though I didn't pass a ReactClass<DefaultProps, Props, State>
const testFunction2 = React.createElement(ExampleFunction, { foo: 1 })

@samwgoldman
Copy link
Member

Not a solution, but just to explain the way things are right now: the current React definitions were designed with React 0.13 in mind. I don't have all the inside scoop here, but personally I am hoping that we roll the 0.14 type definitions into React itself, to avoid the versioning nightmare that comes from vendoring React's interfaces directly into Flow.

@bebraw
Copy link

bebraw commented Dec 11, 2015

Any update on this? Is it possible to annotate function based components now?

@bebraw
Copy link

bebraw commented Dec 25, 2015

I ended up doing this for now:

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

export default (props: {
  notes: Array<Object>,
  onValueClick: Function,
  onEdit: Function,
  onDelete: Function
}): ReactElement => {
  const {notes, onValueClick, onEdit, onDelete} = props;

  return (
    ...
  );
}

Does the approach look good to you? Any gotchas apart from destructuring?

@AsaAyers
Copy link
Author

I can't verify because I was forced to simply abandon Flow, but the core problem was that with 0.13 components typing <Foo /> in one file would be able to tell you you're missing parameter bar, or that it's the wrong type or whatever. From the best I remember of what I found if your component is a function Flow couldn't make the connection between <Foo /> and what function Foo required. I was able to verify internal consistency of the component, but not that it's actually being called correctly from another file.

If I understand your attempt correctly I think it will have the same problem. Flow won't catch missing or wrong parameters in <YourComponent /> style calls.

@bebraw
Copy link

bebraw commented Dec 25, 2015

@AsaAyers Alright. Thanks for getting back to me.

There's actually an extra problem I forgot to mention. It should be possible to set some sane default values for the parameters. For example that onDelete might be undefined (probably need to change type annotation). In this case it would be nice to default to () => {} (noop).

Maybe it makes sense to use propTypes for function based components for the time being. I know you'll be missing out some of the benefits of Flow given then we end up checking the types on runtime, but it's better than nothing. And it solves the problem of default values well.

@thorbenandresen
Copy link

No updates in here for a month, so is it now possible to annotate stateless functional components?

This here does not produce any flow error:
https://gist.github.com/Thorbenandresen/510af38264b0e34b2f5e

@iamdustan
Copy link
Contributor

Nope. Still not possible. No React or Flow releases have happened in the past month either, but it will likely be a release or two before a solution is reached.

Disclaimer: I’m not on either team in any capacity.

@Nor145
Copy link

Nor145 commented Feb 10, 2016

It seems this solve the problem:

Only today discovered that Flow actually supports type-checking of props passed to React components, but you have to add a line like ./node_modules/react(-dom)?/. to your .flowconfig [ignore] to make it work, because otherwise the React library (without type definitions) overrides Flow's built-in React definitions. I'm really surprised there's such a useful feature hidden by an issue like this.

[https://github.com//issues/676]

@Nor145
Copy link

Nor145 commented Feb 10, 2016

@bebraw your approach now works, flow throws an error.

is there any way I can set a default value here? this obv doesn't work..

const Header = (props:  {
  isAdmin: boolean = false,
  dispatch: Function
}) =>  (
  <Toolbar style={style.bar}>
  ...

By the way, I came here from your book, It's excellent!

@chrisui
Copy link

chrisui commented Feb 10, 2016

@Nor145 your syntax is a little wrong here. Default values shouldn't be part of the type definition! :)

const Header = (props:  {
  isAdmin: boolean,
  dispatch: Function
} = {isAdmin: false}) =>  (
  <Toolbar style={style.bar}>
  ...

Would provide a default for the props parameter but that's no good if you pass {dispatch} since it gets overridden. If you just want a default for one or two properties in an object parameter you can use destructuring and default values like the following:

const Header = ({isAdmin = false, ...props}:  {
  isAdmin: boolean ,
  dispatch: Function
}) =>  (
  <Toolbar style={style.bar}>
  ...

@chrisui
Copy link

chrisui commented Feb 10, 2016

That's assuming flow handles the left side being an object anyway!

@Nor145
Copy link

Nor145 commented Feb 10, 2016

@chrisui Thanks for answering. Your second approach is right, but seems there is not support for this feature yet.
[https://github.com//issues/183]

@samwgoldman samwgoldman self-assigned this Feb 16, 2016
@kamek-pf
Copy link

kamek-pf commented Mar 6, 2016

On a side note, it looks like the new comment style syntax doesn't work with stateless components.

@samwgoldman samwgoldman changed the title How do you annotate Stateless Functional React components? Support Stateless Functional React components Mar 16, 2016
@gcazaciuc
Copy link

Is there an available workaround to enable flow checks on Stateless Function components from JSX until proper support is added ?
Given that it's considered best practice to use SFCs as much as possible this issue seems pretty critical to be fixed.

@burabure
Copy link

burabure commented Apr 28, 2016

Just wanted to say that flow has constantly faced issues with new syntax, which has never enabled me or most of the teams I know to have proper type coverage. This issue in particular and the lack of core team communication and "openness" made me take the decision of dropping flow from all projects I have some control over today.

@glortho
Copy link

glortho commented May 2, 2016

I wonder if @marudor has a suggested answer to your question @gcazaciuc ? I could really use this too.

@marudor
Copy link
Contributor

marudor commented May 3, 2016

@glortho sorry, can't help with that.
Some quick tests I tried didn't work.

@kristian-puccio
Copy link

a66c764

Umm wow!

@kristian-puccio
Copy link

This can be closed

@c089
Copy link

c089 commented May 30, 2016

Not sure about leaving this open or creating a new issue but I think this is not quite finished, for example, the documentation on react still says "Support for stateless functional components is coming soon". After reading the documentation on the other ways of building react components, I thought flow could read the propType definition and I didn't have to convert all existing code to use flow annotations? This module fails with flow 0.26.0:

// @flow
import React from 'react';
const Y = (props) => <div>{props.bar}</div>;
const X = (props) => <Y bar={props.foo} />;
X.propTypes = {
  foo: React.PropTypes.string.isRequired
};
export default X;

that it would type check, but flow fails with:

src/app/foo.js:6
  6: const X = (props) => <Y foo={props.foo} />;
                ^^^^^ parameter `props`. Missing annotation

@kristian-puccio
Copy link

I'm not using propTypes I'm using flow type annotations with stateless
components and it works great.

So maybe it's a new issue?

On 30 May 2016 at 16:19, Christoph Neuroth notifications@github.com wrote:

Not sure about leaving this open or creating a new issue but I think this
is not quite finished, for example, the documentation on react
http://flowtype.org/docs/react.html#_ still says "Support for stateless
functional components is coming soon". After reading the documentation on
the other ways of building react components, I thought flow could read the
propType definition and I didn't have to convert all existing code to use
flow annotations? This module fails with flow 0.26.0:

// @flow
import React from 'react';
const Y = (props) =>

{props.bar}
;
const X = (props) => ;
X.propTypes = {
foo: React.PropTypes.string.isRequired
};
export default X;

that it would type check, but flow fails with:

src/app/foo.js:6
6: const X = (props) => ;
^^^^^ parameter props. Missing annotation


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#1081 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/ACA_f-4auRjSM2PjPdN3JHLv0EU3uQwOks5qGoGKgaJpZM4GgUev
.

@c089
Copy link

c089 commented May 30, 2016

I'm not using propTypes I'm using flow type annotations with stateless
components and it works great.

Hm, I'm a bit confused about the feature then: When not using propTypes, a stateless component is just a plain JS function that happens to return a React.Element and adding flow type annotations to this would have worked before 0.26 then, wouldn't it?

@kristian-puccio
Copy link

It's a little different as you don't explicitly call the function, it gets
compiled from jsx. At least in my case that is.

But it wasn't working before and is now as of 0.26

On 30 May 2016 at 16:26, Christoph Neuroth notifications@github.com wrote:

I'm not using propTypes I'm using flow type annotations with stateless
components and it works great.

Hm, I'm a bit confused about the feature then: When not using propTypes,
a stateless component is just a plain JS function and adding flow type
annotations to this would have worked before 0.26 then, wouldn't it?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#1081 (comment), or mute
the thread
https://github.com/notifications/unsubscribe/ACA_fxrC5P0RixkvIF6ME1_eLA0fOHMlks5qGoMTgaJpZM4GgUev
.

@samwgoldman
Copy link
Member

The docs are outdated. I am going to update them soon to reflect our support for stateless functional components.

Regarding PropTypes, Flow understands PropTypes for components created using React.createClass but currently not for components declared using classes or stateless functional components. For classes and functions it's possible to use Flow type annotations, which are recommended.

@c089
Copy link

c089 commented May 31, 2016

@samwgoldman thanks for the explanation and your work 💖

@ghost ghost closed this as completed in 253beb3 Jun 1, 2016
@c089
Copy link

c089 commented Jun 1, 2016

@samwgoldman awesome, thanks. there is one more thing I can't figure out right now: how can we handle the implicit children prop? This fails:

/*:: type XProps = { children: React.Element[] }; */
const X = ({children}/*:XProps*/) => (<div>{children}</div>);
const Y = () => (<X><span>children</span></X>);
 39: const Y = () => (<X><span>children</span></X>);
                      ^^^ React element `X`
 38: const X = ({children}/*:XProps*/) => (<div>{children}</div>);
                             ^^^^^^ property `children`. Property not found in
 39: const Y = () => (<X><span>children</span></X>);
                      ^^^ props of React element `X`

@taylorlapeyre
Copy link

Having the same issue as this ☝️ - would love a solution!

@ctrlplusb
Copy link

Should this issue be reopened for the children case described above?

@ctrlplusb
Copy link

Never mind, its been raised in #1964 and #1934

@ericsoco
Copy link

For future spelunkers, here's the docs for typing functional components.

@keithjgrant
Copy link

Updated link, for reference: https://flow.org/en/docs/react/components/#toc-stateless-functional-components

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests