降档 🏎是你建立简单、灵活、符合WAI-ARIA标准的React自动完成/typeahead/dropdown/select/combobox/etc(又称 "项目选择")(p)react ⚛️组件所需的基础。来自PayPal 💙
网络上的一个常见的组件是自动完成(autocomplete):

在过去的4年中,我个人已经实现了5个独特的自动完成的抽象 😱 这不是一个容易搞定的组件。当你忙着做这些工作的时候,不要忘了让它可以被访问!这也是一项相当大的工作。
幸运的是,我们有相当多的解决方案来实现像自动完成这样的项目选择。具体到React,有react-select、react-autosuggest、react-autocomplete,等等。现在又有一个出现了。它叫做downshift,它的表情符号是赛车🏎,它采取了一种不同的方法。
项目选择的现状
React中现有的项目选择输入(自动完成/typeahead/dropdown/select/combobox/etc)组件的解决方案将为你渲染输入和菜单。其中一些允许你指定你想要渲染的内容,但会在预先确定的位置(或可配置的位置)进行渲染。其中一些还公开了一个类名的列表,你可以参考这些类名来设计渲染的组件,这样你就可以使它符合你的品牌。
不幸的是,这导致了一个更广泛的API表面区域和一个更复杂的实现。即使提供了所有的灵活性,要让这些东西在何时何地以你想要的方式呈现,仍然是一件非常麻烦的事情。
这些问题和限制主要是由于这些库在为你做渲染(React.createElement)的事实。
下移🏎如何不同
有两个设计决定提供了关于downshift ,使其有别于现有解决方案的显著特征:
- 渲染道具 "模式。
- "受控道具 "模式。
让我们来谈谈这两者是如何为你带来难以置信的价值和灵活性的。
渲染回调
中,实际上没有一个React.createElement(或JSX)的实例。 downshift 源代码*...*相反。downshift 这让你可以在<Downshift />.NET中渲染任何你想要的东西。它还让你完全控制了何时、何地以及如何渲染菜单中的可选项目。
这意味着downshift ,几乎不需要像其他解决方案那样暴露出很多道具,因为没有渲染需要配置。
事实证明,不需要渲染就可以增加简单性,减少包的大小,并增强灵活性。
那么,它是什么样子的呢?
下面是一个带有downshift 的赤裸裸的自动完成组件:
import * as React from 'react'
import {render} from 'react-dom'
import Downshift from 'downshift'
const items = [
{value: 'apple'},
{value: 'pear'},
{value: 'orange'},
{value: 'grape'},
{value: 'banana'},
]
render(
<Downshift
onChange={selection => alert(`You selected ${selection.value}`)}
itemToString={item => (item ? item.value : '')}
>
{({
getInputProps,
getItemProps,
getLabelProps,
getMenuProps,
isOpen,
inputValue,
highlightedIndex,
selectedItem,
}) => (
<div>
<label {...getLabelProps()}>Enter a fruit</label>
<input {...getInputProps()} />
<ul {...getMenuProps()}>
{isOpen
? items
.filter(item => !inputValue || item.value.includes(inputValue))
.map((item, index) => (
<li
{...getItemProps({
key: item.value,
index,
item,
style: {
backgroundColor:
highlightedIndex === index ? 'lightgray' : 'white',
fontWeight: selectedItem === item ? 'bold' : 'normal',
},
})}
>
{item.value}
</li>
))
: null}
</ul>
</div>
)}
</Downshift>,
document.getElementById('root'),
)

这是一个相当简约的例子,如果你想用其他自动完成库来构建类似的简约例子,你当然可以用更少的代码行来完成它。但是你会注意到,我们只把onChange和render 道具传给了<Downshift /> 。render 道具是一个函数,它被调用时带有一些辅助方法和状态,以便我们建立我们的组件。**downshift** 负责管理用户交互、状态和大部分的可访问性,而我们负责根据这些状态来渲染东西。
你的自动完成组件实际上是一个由downshift
的状态的函数。
关于上面的例子,你会注意到的另一件事是,render 函数传递的不仅仅是状态。你还得到了getInputProps 和getItemProps 。这些是 "道具获取器"(灵感来自Jared Forsyth),*它们是允许你渲染你喜欢的东西的关键。*只要你把所有的道具转发给你要渲染的相应元素(如果你要渲染的话),那么downshift 就会把所有的事情联系起来。
另外,因为downshift 没有渲染你的菜单或项目,downshift不需要为你如何过滤或加载它们提供任何API。你可以异步加载它们(使用Apollo和graph.cool的例子和使用Algolia Instantsearch的这个例子),你可以控制你如何过滤东西(见这个整合了geniejs的例子,看我在这里建立geniejs的整合)。 这意味着你不需要学习或围绕API工作,可以以最适合你使用情况的方式进行。
这个API也意味着你根本不需要渲染一个输入。因此,你也可以使用downshift 来实现一个下拉菜单,而不会有任何麻烦。下面是一个实现多选下拉菜单的例子,用的是downshift.
还有其他可用的道具获取器(有些只是为了使可访问性更容易)。参见downshift docs获取更多信息。
受控道具
downshift 的另一个设计决定是使用受控道具。
如果你使用过React一段时间,你可能已经碰到了受控和不受控组件的概念。其中最常见的是<input /> 组件,如果你想控制输入值是什么,它允许你指定一个value 道具。如果你指定了这个道具,那么你就要负责保持它的更新(通常这需要一个onChange 处理程序来保持与用户更新时的同步)。
downshift 对于它所追踪的所有状态片断, 、 、 、 ,都有这个完全相同的概念。这些信息是你可以在你的 函数中访问的,还有一个 回调。但有时(就像 )你需要能够完全控制它。因此,如果你把这些东西中的任何一个作为道具提供给 组件(例如 ),它就变成了 "受控", 将引用你的道具的值,而不是在内部用状态跟踪它。isOpen selectedItem inputValue highlightedIndex render``onStateChange <input /> downshift <Downshift isOpen={true} /> downshift
这使得你可以完全控制你的组件的状态。Ryan Florence在这堂精彩的课程中讲授了关于可控组件(如downshift)的精彩课程(我强烈推荐)。你可以在这里看到我建立的第一个迭代的实现。
可及性
可访问性(#a11y)是一个非常重要的功能,坦率地说,对于像自动完成这样的项目选择组件来说,要做到这一点是不容易的。在开发过程中,我参考了几个自动完成组件,Marcy Sutton很好地给我们的一个例子做了无障碍性审核(谢谢你Marcy!)。用VoiceOver调出一个例子,我想你会留下深刻的印象!我们努力确保它是无障碍的(在这里看我的工作),根据我对现有解决方案的调查,downshift 是同类产品中最无障碍的组件。
尺寸
downshift UMD的构建文件只有14.34kb(未压缩),比其他类似的解决方案要小得多。因为 ,你可以完全控制渲染,所以需要的代码要少得多。此外,使用downshift preact⚛️(一个很小的react版本,不需要添加preact-compat)很容易实现开箱工作。我能够使用preact-habitat创建一个实验,它给出了一个完全无框架的自动完成功能的实现 不到26kb(未压缩)。这个大小包括 downshift +preact +preact-habitat 。你有我的许可,可以高兴地跳起来😉。在此向Jason Miller和Zouhir表示感谢。 preact和preact-habitat!
它在哪里?
我为PayPal上的这个国家选择器体验建立了downshift 。

我们也在收件人选择器中使用相同的组件。

实际上,我们的应用中还有其他几个项目选择体验,它们的用例略有不同,因此我们有必要在同一个应用中对自动完成组件进行多次实现所以这就是我建立downshift的原因:这样我们就可以有一个足够灵活的单一实现来覆盖所有的用例。PayPal的体验应该在下周发货。
贝宝的其他团队现在也正在将downshift与他们的应用程序整合。
我大约在一个月前开始了downshift的工作,第一个测试版在第二天就发布了(作为react-autocompletely)。在正式发布1.0.0版本之前,它就已经慢慢受到欢迎了(它已经有900个🌟和7k的下载量/月)!因此,它肯定会被应用在其他领域。所以它肯定被用在好几个地方,但我知道的第一个生产部署是在codesandbox,来自Ives van Hoorne(gif来自他的推特)。

有趣的是:这两个解决方案也都使用了match-sorter,这是一个用户友好的最佳匹配排序库。我强烈推荐它!
下一步是什么
downshift 在设计上是一个相当小的组件。它确实需要多做一点工作,才能得到你从其他解决方案中开箱即用的东西。但我相信你可以在 上建立所有这些功能。所以我希望社区可以帮助建立 -powered 版本的一些功能,并将其发布到npm上。downshift downshift这里是一个开始的好地方。
总结
我需要向Ryan Florence致以崇高的敬意。他关于"复合组件 "的课程让我开始了这个工作。downshift 实际上已经不使用复合组件了,但第一个版本是这样的!因此,感谢Ryan!他的课程让我开始了这个工作。所以要感谢Ryan!你可以在这个YouTube播放列表中看到我开发的大部分downshift (从这第一个视频开始)📺。
还有,再次向Jared Forsyth表示感谢,因为有一天我们在机场偶遇,他给了道具获取者很大的启发 ✈️这对API的方向也有很大的影响。
特别感谢Travis Arnold、Julien Goux、the_Simian和所有的贡献者(到目前为止),感谢他们的帮助,使downshift API形成现在的样子。