Consider the following example using flow props.
import * as React from 'react';
type FooProps = {|
foo: number,
bar?: string
|};
class Foo extends React.Component<FooProps> {}
We have React component class Foo
that accept an exact object of props. We want to enforce exactness so that users are not inadvertently making typos on their props (e.g. baz
when they meant to use bar
).
This works absolutely fine and errors happen as expected.
However, what if we want to spread props to this component from somewhere else? For example:
const f = (props: FooProps) => <Foo {...props} />;
Flow will give us an error about exactness on props
:
10: const f = (props: FooProps) => <Foo {...props} />;
^ Cannot create `Foo` element because inexact props [1] is incompatible with exact `FooProps` [2].
References:
10: const f = (props: FooProps) => <Foo {...props} />;
^ [1]
8: class Foo extends React.Component<FooProps> {}
^ [2]
Disregarding the argument, "you shouldn't spread props to components like that when you're asking for exactness", how can spreading be achieved?
I did find one way to do this, but it uses an undocumented utility type $Shape<T>
(code). It's unclear if this has any consequences or side effects, but it appears to work correctly:
class Foo extends React.Component<$Shape<FooProps>> {}
Here's a link to try out what I've got using $Shape<T>
with items I expect to error (and not):
export default App; Basically that's how props are passed from component to component in React. As you may have noticed, props are only passed from top to bottom in React application's component hierarchy. There is no way to pass props up to a parent component from a child component.
You can pass a component as props in React by using the built-in children prop. All elements you pass between the opening and closing tags of a component get assigned to the children prop.
To pass data from a child component to its parent, we can call a parent function from the child component with arguments. The parent function can be passed down to the child as a prop, and the function arguments are the data that the parent will receive.
Lift state up. Lifting the state up in the component tree can help minimize prop drilling. You remove the need to pass more props down to child components by lifting the state that depends on those props to the parent. It also makes the state more flexible and easier to share between multiple child components.
Apologies if this is not from an official source.
Solution: Type cast props to any
before spread.
It meets your requirements of spread within a function. The function ensures exactness prior to type casting.
NB: $Shape<FooProps>
on the cast does not work, it still thinks its exact. Neither does assigning props
to a const spreadable: $Shape<FooProps> = props
It seems $Shape
doesn't remove the exactness, despite appearances in your example. One way or another, you have to strip the exactness, and doing it internally the function seems plausible.
The Flow docs discuss type casting via any
as legitimate although potential unsafe and not recommended (because you loose type safety). I think in the context its reasonable.
import * as React from 'react';
type FooProps = {|
foo: number,
bar?: string
|};
class Foo extends React.Component<FooProps> {}
const f = (props: FooProps) => <Foo {...(props: any)} />;
f({foo: 1}) // PASS: with foo, without bar
f({foo: 1, bar: ''}) // PASS: with foo and bar
{ <Foo foo={1} /> } // PASS: with foo, without bar
{ <Foo foo={1} bar="" /> } // PASS: with foo and bar
f({}) // FAIL: missing foo
f({foo: ''}) // FAIL: wrong type of foo
f({foo: 1, bar: 1}) // FAIL: with foo, wrong type of bar
f({foo: 1, x: 1}) // FAIL: unexpected x
{ <Foo /> } // FAIL: missing foo
{ <Foo foo="" /> } // FAIL: wrong type of foo
{ <Foo foo={1} bar={1} /> } // FAIL: with foo, wrong type of bar
{ <Foo foo={1} x={1} /> } // FAIL: unexpected x
Try it here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With