【问题标题】:How can I spread props to a React component that uses exact props when using Flow?如何在使用 Flow 时将 props 传播到使用精确 props 的 React 组件?
【发布时间】:2018-07-28 07:33:33
【问题描述】:

考虑以下使用流道具的示例。

import * as React from 'react';

type FooProps = {| 
  foo: number,
  bar?: string 
|};

class Foo extends React.Component<FooProps> {}

我们有 React 组件类 Foo,它接受一个精确的 props 对象。我们希望强制执行准确性,以免用户无意中在他们的道具上打错字(例如,baz 当他们打算使用 bar 时)。

这绝对可以正常工作,并且会按预期发生错误。

但是,如果我们想从其他地方向这个组件传播 props 怎么办?例如:

const f = (props: FooProps) => <Foo {...props} />;

Flow 会在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]

不考虑“当你要求精确性时,你不应该将 props 传播到这样的组件”的论点,如何实现传播?

我确实找到了一种方法来做到这一点,但它使用了一个未记录的实用程序类型$Shape&lt;T&gt; (code)。目前尚不清楚这是否有任何后果或副作用,但它似乎可以正常工作:

class Foo extends React.Component<$Shape<FooProps>> {}

这是一个link to try out what I've got using $Shape&lt;T&gt;,其中包含我预计会出错(而不是)的项目:

【问题讨论】:

  • $Shape 变体不只是使组件采用不精确的道具,而您的功能采用精确的吗?所以你还没有解决让组件采用精确道具的问题。我的理解是创建接受精确道具的组件是不明智的。未引用的道具不是问题。错误输入 prop 名称意味着缺少预期的 prop 值(在需要时),因此会产生错误。
  • @DaveMeehan 这正是我所说的 $Shape 具有意想不到的后果的意思。考虑到各种用例,输入错误的可选道具可能会出现问题。这种错误可能很小,例如缺少视觉提示,也可能很大,例如收入损失。

标签: javascript reactjs flowtype


【解决方案1】:

抱歉,如果这不是来自官方来源。

解决方案:在传播之前将 cast props 键入any

它满足您在函数内传播的要求。该函数在类型转换之前确保准确性。

注意:$Shape&lt;FooProps&gt; 在演员表上不起作用,它仍然认为它是准确的。也没有将props 分配给const spreadable: $Shape&lt;FooProps&gt; = props 似乎$Shape 并没有消除准确性,尽管在您的示例中出现了。无论哪种方式,您都必须去除准确性,并且在内部执行该功能似乎是合理的。

The Flow docs discuss type casting via any as legitimate 尽管潜在的不安全且不推荐(因为您失去了类型安全性)。我认为在上下文中它是合理的。

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

【讨论】:

  • 谢谢,@dave-meehan。我很欣赏这个解决方案,我会接受它,因为它似乎是唯一没有副作用的解决方案,除了可能无法正确键入 f 函数上的 props 参数。
  • 尝试了多种方法来实现这一目标,这是迄今为止唯一可行的解​​决方案。谢谢!
【解决方案2】:

看起来这是一个很常见的问题:Exact fails when doing es6 object spread

整个线程中建议的一个选项是使用:

type Exact<T> = T & $Shape<T>

那么不要{||}写你的类型:

type FooProps = {
  foo: number,
  bar?: string 
}

class Foo extends React.Component<Exact<FooProps>> {}

这将允许您使用扩展运算符,并且仍然可以自动完成属性。

【讨论】:

  • 使用这种模式在其他地方有一些问题,所以它不是一个很好的推荐解决方案,因为它带有很多“何时何地不使用”example
  • 我认为Exact&lt;T&gt; = T &amp; $Shape&lt;T&gt; 是一个非常弱的精确性:它在没有传播的世界中表现得就像精确性一样,当仅限于传播场景时,它将完全无用(即等同于 T)。因此,当您想保护您的组件免受并非源自传播场景的额外道具的影响时,它很有用,而且当涉及传播时也不会像 $Exact 那样抱怨。我可能错了:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-18
  • 1970-01-01
相关资源
最近更新 更多