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

property children not found in props of React element ... #1964

Closed
MoOx opened this issue Jun 17, 2016 · 65 comments
Closed

property children not found in props of React element ... #1964

MoOx opened this issue Jun 17, 2016 · 65 comments
Labels

Comments

@MoOx
Copy link

MoOx commented Jun 17, 2016

This issue showed when I upgraded from 0.25 to 0.27...

I think I am still having issue.

Here is a component

// @flow

import React from "react"
import { StyleSheet, View } from "react-native"

type Props = {
  children: ReactElement,
}

const NavigationBar = (props: Props): ReactElement => {
  return (
    <View
      accessibilityRole={ "menubar" }
      style={ styles.bar }
    >
      <View style={ styles.center }>
        { props.children }
      </View>
    </View>
  )
}

NavigationBar.defaultProps = {
  children: null,
}

const styles = StyleSheet.create({
  // ...
})

export default NavigationBar

And here is a place where it is throwing errors

// ...
const Header = (props: Props): ReactElement => {
  const { user } = props

  return (
    <NavigationBar> // <- HERE I AM THE ERROR: property `children` not found in props of React element `NavigationBar`
      <View style={ styles.left }>
// ...

Looks like a bug. Or maybe I am doing something wrong?

@Kerumen
Copy link

Kerumen commented Jun 22, 2016

Dupe of #1934

@ctrlplusb
Copy link

ctrlplusb commented Jul 12, 2016

For now I have had to resort to the following:

type Props = {
  children?: ReactElement,
  other: number,
};

function Foo({ children, other } : Props) {
  return (
     <div>...</div>
  );
}

@szagi3891
Copy link

fast offtopic :
ReactElement is the proper way to describe output type from function render ?
where it is defined by this definition ?

@jedwards1211
Copy link
Contributor

jedwards1211 commented Jul 29, 2016

@ctrlplusb you can workaround without using a maybe prop:

<NavigationBar children={<span>flow bug (this will get overridden by the children below)</span>}>
  <View style={ styles.left }>
  ...
</NavigationBar>

@jedwards1211
Copy link
Contributor

@szagi3891 It's here: https://github.com/facebook/flow/blob/master/lib/react.js#L32

I thought it has to be React.Element in recent versions of flow though?

@ekosz
Copy link

ekosz commented Sep 14, 2016

Is there any update on this issue? I just just ran into this.

@MoOx
Copy link
Author

MoOx commented Sep 20, 2016

My real poor workaround is to always do children?: React$Element<any>.

@wildeyes
Copy link

@MoOx Hey, why do you write the dollar sign between React and Element?

@gcanti
Copy link

gcanti commented Sep 23, 2016

@wildeyes React$Element and import type { Element } from 'react' are equivalent https://github.com/facebook/flow/blob/master/lib/react.js#L226

Macil added a commit to StreakYC/react-draggable-list that referenced this issue Sep 26, 2016
Cut out extra flowignore lines that are no longer necessary now that
react-motion uses flow.

Use `children` prop to work around
facebook/flow#1964
@mqklin
Copy link

mqklin commented Oct 8, 2016

My workaroud:

type Props = {
  children: React$Element<*>,
};

class Div extends Component {
  props: Props;
  render() {
    const { props } = this;
    return (
      <div>{props.children}</div>  
    );
  }
}

Usage:

  <Div
    children={(
      <span>Works nicely</span>
    )} 
  />

@Ehesp
Copy link

Ehesp commented Oct 19, 2016

Hitting this issue... bread and butter React syntax. Work arounds feel a bit stinky :(

@rosskevin
Copy link

rosskevin commented Oct 19, 2016

This syntax works for me and I believe was the recommended change when upgrading from 0.25.0

// @flow
import React, {Element} from 'react' // EDIT this should be importing type!!

type Props = {
  children: Element<any>
}

And the signature for render is

render (): Element<any> 

Am I missing something in the question/problem? I made this change quite some time ago and it is working well. This doesn't seem like a workaround.

EDIT: I've left the above for posterity, but after more use it seems that we should be using import type {Element} from 'react' and Element<*>, see my later comment.

@Ehesp
Copy link

Ehesp commented Oct 19, 2016

@rosskevin Well I'm fairly new to Flow at the moment, so maybe I'm not understanding correctly! Here's my example:

/**
 * @providesModule Button
 * @flow
 */

import React, { PropTypes } from 'react';
import { TouchableHighlight, Text } from 'react-native';

function Button({ onPress, children }: { onPress: () => void, children: string }) {
  return (
    <TouchableHighlight
      onPress={onPress}
    >
      <Text>
        {children}
      </Text>
    </TouchableHighlight>
  );
}

Button.propTypes = {
  onPress: PropTypes.func,
  children: PropTypes.string.isRequired,
};

export default Button;

So in this, I'm saying by Button must always have children which must always be a string. When I implement this in another component like so...

<Button onPress={this.someFunc}>
    Click Me
</Button>

... I get a Flow error of Flow children: Property not found in props of React element "Button".

The only "fix" I can see right now is changing children: string to children?: string, which is removing the error, but now Flow thinks a children prop maybe has to be set (which it isn't), when in fact I always want to have children, just not as a direct prop, rather than in-between my <Button> component.

EDIT: Same issue with using Element<string>. Also it doesn't really work with the above, since now I can pass in any Element (View, Text etc) as the child (between the component) and it doesn't complain.

@rosskevin
Copy link

rosskevin commented Oct 19, 2016

@Ehesp - I suggest two things

  • use Element<any> for children
  • stop doing propTypes separately and use flow-react-proptypes (before your babel entry for transform-flow-strip-types. Here's my .babelrc

Here's how I would refactor your component:

// @flow
import React from 'react'
import pure from 'recompose/pure'
import { TouchableHighlight, Text } from 'react-native'

type Props = {
  onPress: Function,
  children: Element<any>
}

export default pure((props: Props): Element<any> => {
  const { onPress, children, ...rest } = props
  return (
    <TouchableHighlight onPress={onPress}>
      <Text>
        {children}
      </Text>
    </TouchableHighlight>
  )
})

This is untested but I think it should work for you.

@AsaAyers
Copy link

JSX converts to something like this:

React.createElement(
    Button,
    { onPress: thissomeFunc },
    "Click Me"
);

Flow recognizes that the 2nd parameter is where props come from, but it doesn't seem to recognize that the 3rd parameter gets mixed into props under the key children.

This workaround works because you're moving children from the 3rd parameter into the 2nd parameter

  <Div
    children={(
      <span>Works nicely</span>
    )} 
  />

becomes

React.createElement(Div, {
  children: React.createElement(
    "span",
    null,
    "Works nicely"
  )
});

If you use children?: React$Element<any>. the error goes away. Flow still thinks children is always missing, but now that it's marked optional it doesn't complain.

@AsaAyers
Copy link

AsaAyers commented Oct 19, 2016

@rosskevin I corrected your untested code here. You can see that at the bottom I am passing children to Pure but Flow just doesn't see it.

I dumped the : Element<any> part because it was just giving me errors I didn't need to fix.

@rosskevin
Copy link

@AsaAyers - I corrected your correction ;) with imports here

I haven't seen this problem but it does seem that you have to make Element<any> optional.

Here is a reduced test case that seems to prove either this is a bug (or my misunderstanding).

@Macil
Copy link
Contributor

Macil commented Oct 19, 2016

That doesn't work for elements that actually require children.

react-motion specifically requires a function to be passed as the children prop.

@ghost
Copy link

ghost commented Nov 4, 2016

So apparently the fix: a7878eb was reverted in 0185892, which means that flow currently can't check whether the children prop has been set in jsx.

From the revert:

We don't have a way to declare the type of a React element "nominally" based on it's type. Without this, it's arguably better to be ignorant than support JSX children.

Still, eventually supporting jsx children would be nice, it's a pretty crucial part of using react. Any feedback on whether there will be support for this in the future?

@AsaAyers
Copy link

AsaAyers commented Nov 4, 2016

Every few months I come back and try Flow to see if it's ready. When trying to add Flow to my codebase it never takes me long to find the show stoppers. It seems I can only use flow if I significantly change the style of my code to avoid JS features where Flow is buggy. I guess I'd need to drop JSX to avoid this.

If this were the only problem I might consider one of the workarounds here. But for now I'm back to subscribing to issues and I'll try again in a few months when some of them close.

@steida
Copy link

steida commented Nov 29, 2016

@AsaAyers I recently flowtyped my relatively huge project. You can check it. https://github.com/este/este

@steida
Copy link

steida commented Nov 29, 2016

@AsaAyers If you are not sure what to use, use any. Even /* @flow weak */ is immensely useful. Or check how I flowtyped Fela robinweser/fela#134

nsimonson added a commit to nsimonson/react-motion that referenced this issue Dec 9, 2016
A bug in flow (facebook/flow#1964) prevents it checking in it's children prop has been set in jsx. You can make flow ignorant to this prop by setting it as optional.

Addresses chenglou#375
@cjke
Copy link

cjke commented Apr 16, 2017

I do agree - and it is painful. Especially considering they come from the same parent.

However, I do feel that it is a rabbit hole. Unless they can define the relationship between an argument and a property on another argument abstractly, or we are provided a mechanism to define required props.children and children in ours components separately, I don't see how this could be added without bloating flow to handle specific library use cases (in this case React).

In saying this, I am no Facebook engineer, I'm a pleb with a laptop - so I'm sure they will figure out something that avoids tightly coupling flow to react while resolving this issue.

@suchipi
Copy link

suchipi commented Apr 16, 2017

Off the top of my head, here's some places where different uses of JSX differ:

  • Is the third argument used as an attribute called children?
  • When there is only one child JSX element, is it passed as a single-item array to the factory function, or a bare item?
  • How are uppercase/lowercase element type names treated? (React uses identifiers for uppercase and strings for lowercase)

You could theoretically make configuration for each of these in .flowconfig:

[options]
jsx.useChildrenAttribute=true
jsx.singleChildWrappedInArray=false
jsx.identifierTypeMatcher=^[A-Z]\w+

But in practice, most people just want

[options]
jsx.react=true

@aaronjensen
Copy link

Could it be typed something like this?

/* @flow */

type Props = {
  foo: string,
  children: number
}

type RC<P> = (props: P) => void

declare var Foo: RC<Props>
 
declare function createElement<C, P: { children: C }>(component: RC<P>, props: $Diff<P, {children: C}> & {children?: void}, children: C): void
declare function createElement<P>(component: RC<P>, props: P, children: void): void

createElement(Foo, {foo: 'hi', children: 3})
createElement(Foo, {foo: 'hi'}, 3)

https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgAKATnDgM5gC8YA3qmGFHHAFxjkbECWAdgOYAaBmADGAC24wAJsTy92vAK4BbAEZ5iqAL7ps+MACUAwgB5CAPmpgAFDlIV2hAJTUrANzjdp6aXlEwAIZyYO7BYABiLOwm5g7kFgyofgHBBFBKvKIY3HC8YnKBGHgAojB4KvIYpsaCROy0YpIycgpgxmDaFjaicCo4eVUxZpZ19mTk7AAkACLcUFDmdbQSUrLy7MZdYABkdKst8gD87J7e2nUH623GzqdePilBIRlZOXkFeEWl5ZW81ZYen0BrwhkYRhYxvEnJdmtd7t47qEHuhRIVimUKlUbFE4MtmGwwABySRE2FrVrsADM2mcqDRXwxv2xuPx0WJpIuYCpziAA

@daltones
Copy link

@aaronjensen seems like a good start. But Flow still doesn't “understand” that JSX children would be the third argument of createElement.

Besides that, these declarations don't handle multi-element children the way React.createElement is.

<Foo>
  <i />
  <i />
  <i />
</Foo>

// equals to:
createElement(Foo, null, createElement('i'), createElement('i'), createElement('i'));

// and not:
createElement(Foo, null, [createElement('i'), createElement('i'), createElement('i')]);

@aaronjensen
Copy link

@daltones

But Flow still doesn't “understand” that JSX children would be the third argument of createElement.

Really? What does it do with JSX then? I was assuming that it translated it into a React.createElement call (if you use the default pragma). How else would it type proptypes or the children prop? Babel certainly "understands" it.

As for the multiple children, I think you can handle that too:

/* @flow */

type Props = {
  foo: string,
  children: number
}
  
type BarProps = {
  foo: string,
  children: Array<number>
}

type RC<P> = (props: P) => void

declare var Foo: RC<Props>
declare var Bar: RC<Props>

declare function createElement<C, P: { children: C }>(component: RC<P>, props: $Diff<P, {children: C}> & {children?: void}, children: C): void
declare function createElement<C, P: { children: C }>(component: RC<P>, props: $Diff<P, {children: C}> & {children?: void}, ...children: C): void
declare function createElement<P>(component: RC<P>, props: P, children: void): void

createElement(Foo, {foo: 'hi', children: 3})
createElement(Foo, {foo: 'hi'}, 3)
createElement(Bar, {foo: 'hi'}, 3, 3, 3)

@daltones
Copy link

@aaronjensen Yeah! And I guess this is the main bug here: Flow can only handle normal JSX props (name={value}), but cannot handle element children (<Foo>{children}</Foo>).

You can see the issue using your example adding the same elements in JSX syntax.

@SEAPUNK
Copy link

SEAPUNK commented Apr 17, 2017

Considering how Facebook & Co. uses React and (I would assume) also use Flow, I wonder how they handle this problem themselves... 🤔

@aaronjensen
Copy link

Flow can only handle normal JSX props (name={value}), but cannot handle element children ({children}).

Right, which is what we're discussing in this thread. They cannot handle it now, but I'm wondering if one way for them to handle it is with a better signature of React.createElement.

The example you linked doesn't really prove anything because it's still using the built-in React.createElement jsx pragma. Unfortunately, // @jsx doesn't work on flowtype.org for some reason, so I built this: https://github.com/aaronjensen/type-create-element

Try loading that up and changing the children types.

The only thing the signature doesn't handle right now afaik is JSXIntrinsics because I'm not entirely sure how to type those (and maybe they aren't either)

@aaronjensen
Copy link

Typescript just merged support for this: microsoft/TypeScript#15160

@Hypnosphi
Copy link
Contributor

Hypnosphi commented May 4, 2017

React flow typing excludes defaultProps from the config of props that need to be provided to createElement, because they will be assigned by React. Maybe it should also exclude children for the same reasons, like this:

Config: $Diff<$Diff<Props, DefaultProps>, {children: any}>

@Macil
Copy link
Contributor

Macil commented May 4, 2017

@Hypnosphi Sometimes you want to enforce the type of children; having the type be ignored wouldn't help there.

@Hypnosphi
Copy link
Contributor

Hypnosphi commented May 4, 2017

@agentme yes, but normally children are provided separately from props. $PropertyType<Props, 'children'> can be used for their checking.

UPD. The {children: any} in my comment above wasn't intended to ignore the type, it was meant to exclude the children prop from shape. But, because of some $Diff<A, B> bug, B's field types override the corresponding ones of A, so in fact the original type gets lost, and <Component children={/* bad value */} /> stops rising a warning. Something like this should work though:

Config: $Diff<$Diff<Props, DefaultProps>, {children: $PropertyType<Props, 'children'>}>

@karlhorky
Copy link

Just tried @rosskevin 's example again with Flow 0.47.0 and it is not throwing errors currently:

Try Flow Link

screen shot 2017-06-06 at 17 47 33

Is this intentional or actually a bug currently in Flow?


Strangely enough, it doesn't appear to be actually checking the type of the children:

Try Flow - children with type boolean

@echenley
Copy link

echenley commented Jun 6, 2017

@karlhorky I may be mistaken, but wouldn't you have to do this?

import React, { type Element } from 'react'

Otherwise, you could use the global React$Element type and avoid the import altogether.

@karlhorky
Copy link

@rosskevin
Copy link

Infer * might be the key, this seems predictable

@suchipi
Copy link

suchipi commented Jun 13, 2017

Ooo, does that mean this is fixed?

@SEAPUNK
Copy link

SEAPUNK commented Jun 13, 2017

I hope so, even though nobody on the dev team said anything about it. My cynical side expects this to break again in the next releases.

@gaastonsr
Copy link

Almost, it checks for the presence of children but it doesn't do type checking yet.

@agrcrobles
Copy link

Flow v0.53 supports children out of the box!!

import * as React from 'react';

type Props = {
  children?: React.Node,
};

More info: https://flow.org/en/docs/react/children/

@asolove
Copy link
Contributor

asolove commented Aug 22, 2017

Now that Flow v0.53 has improved the Component type and made children work, I think this issue can be closed. (/cc @calebmer)

@MoOx MoOx closed this as completed Aug 22, 2017
mbifulco pushed a commit to workfrom/ex-navigation that referenced this issue Sep 7, 2017
Flow doesn't understand jsx + children
(facebook/flow#1964) so this makes the prop optional
and adds an invariant to make sure it was actually passed.

Closes expo#301

fbshipit-source-id: 76ea6d7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests