这个 "功能 "已从React 16中删除,所以请不要依赖它。尽管如此,这还是有点意思的,所以继续阅读吧
这周我在PayPal的一个内部模块上工作,不得不用React做一些有点古怪的事情。 我想你会有兴趣听听我学到了什么。
不,这并不是关于渲染道具。如果你希望这样......请稍后回拨😉

语境
所以react-i18n (不是npm的那个......是我们在PayPal内部做的那个)有这样一个API,我称之为 "sorta-curried"。我在上次的新闻通讯中写了一些关于它的内容。
所以这里有一个例子:
import getContent, {init} from 'react-i18n'
init({
content: {
pages: {
home: {
nav: {
about: 'About',
contactUs: 'Contact us',
},
},
},
},
})
// here's the sorta-curried part...
// These all result in exactly the same thing: "About"
getContent('pages.home.nav.about')
getContent('pages')('home')('nav')('about')
getContent('pages.home')('nav.about')
getContent('pages')('home.nav')('about')
// etc...
如果你不喜欢这个API,你并不孤单。但是,API是有原因的,这不是我们在这篇文章中要讨论的......现在回想起来,这是个糟糕的API,我不应该这样做😅。
使用React
所以在React的背景下思考这个问题:
const getHomeContent = getContent('pages.home')
const ui = (
<a href="/about">
{getHomeContent('nav.about')}
</a>
)
// that'll get you:
<a href="/about">About</a>
到目前为止还不错。但是,如果你搞砸了,出现了错别字怎么办?
在本周之前,发生的情况是这样的:
const ui = (
<a href="/about">
{getContent('pages.typo.nav.about')}
</a>
)
// that'll get you:
<a href="/about">{pages.typo.nav.about}</a>
// note, that's a string of "{" and "}"...
// not jsx interpolation...
的问题
所以,这很好。但是,这里是事情变得棘手的地方。因为我们返回的是一串{full.path.to.content} ,如果内容丢失或有错别字,你就不能对你返回的内容调用一个函数。如果你尝试,你将在一个字符串上调用一个函数,这将给你一个错误,使应用程序崩溃。错误边界可以帮助解决这个问题,尽管有时我们会在React上下文之外调用getContent ,所以这并不是在所有情况下都有帮助。无论如何,这将破坏应用程序:
const getHomeContent = getContent('pages.typo')
const ui = <a href="/about">{getHomeContent('nav.about')}</a>
// 💥 error 💥
同样,发生这种情况是因为getContent('pages.typo') 将返回字符串{pages.typo} (表示在该路径上没有内容,开发人员需要解决这个问题以获得内容)。问题是,你不能调用一个字符串,但这就是正在发生的事情,因为getHomeContent 是一个字符串,而不是一个函数。
一个解决方案和一个新问题
因此,我本周所做的改变使它在给定的路径上没有内容时,不再是一个字符串,而是返回一个 "sorta-curried "函数(就像你没有打错字时那样)。这样,如果你想的话,你可以一整天都在调用它。没问题。
所以现在这不会出错,但如果没有内容,我们就会失去渲染路径:
const getHomeContent = getContent('pages.typo')
const ui = (
<a href="/about">
{getHomeContent('nav.about')}
</a>
)
// that'll get you:
<a href="/about"></a>
我们要确保显示缺失的内容,这样对开发者来说就更明显了(是的,我们也会记录到控制台),如果世界上发生了🔥🌎🔥🌏🔥🌍🔥,而内容由于某种原因未能加载,那么一个按钮说{pages.transfer.sendMoney} 总比什么都不说要好。
所以,挑战就在这里了。让我们重写一下上面的内容,使之更加清晰。
const getHomeContent = getContent('pages.typo')
const aboutContent = getHomeContent('nav.about')
const ui = <a href="/about">{aboutContent}</a>
aboutContent 在这个例子中是一个函数,因为对 的调用有一个错字,所以我们实际上永远找不到与完整路径相匹配的内容。因此,挑战是我们如何确保在这样的情况下能够呈现完整的路径?getContent
开发解决方案
起初,我以为我可以在内容获取器函数上做猴子补丁toString 。但这并不奏效。我还是从React得到了这个警告。
警告。函数作为React的一个子节点是无效的。如果你返回一个Component而不是从render中返回,这可能会发生。或者你是想调用这个函数而不是返回它。
因此,我在记录该错误的那一行设置了一个断点,并在堆栈中逐步查找问题所在。
> printWarning
> warning
> warnOnFunctionType
> reconcileChildFibers <-- ding ding! 🔔
这个reconcileChildFibers 函数是我发现react会检查你试图渲染的子对象,以确保它们是可渲染的。
纵观这段代码,它首先检查它是否是一个对象,然后检查它是否是一个字符串或数字,然后是一个数组,然后是一个迭代器。如果它不是这些东西,那么它就会抛出(对于非ReactElement对象)或警告(对于一个函数,像我们的例子)。
所以,在我的例子中,由于前面提到的限制,我想要渲染的东西必须是一个函数。所以我不能让它作为一个对象、字符串、数字或数组工作。但我意识到,没有什么能阻止我使我的函数可迭代(如果你不熟悉,这里是我的ES6工作坊录音中的迭代器部分)。
所以......我让我的函数可迭代😉。

const ITERATOR_SYMBOL =
(typeof Symbol === 'function' && Symbol.iterator) || '@@iterator'
// ...
function iterator() {
let timesCalled = 0
// useful logging happens here...
return {
next() {
// this is called twice. Once to get the value, and the second time
// will report that it's done.
return {done: timesCalled++ > 0, value: pathAsString}
},
}
}
// ...
contentGetterFn[ITERATOR_SYMBOL] = iterator
// ...
我为此做了一个方便的函数,并创建了一个CodeSandbox演示,供你试用!请欣赏

这也是一个很酷的事情,我可以用一堆上下文来记录一个错误,以帮助开发者弄清楚发生了什么。这是可能的,因为如果调用iterator ,我可以假设React正在尝试渲染contentGetterFn 。
所以,这就是我让函数可迭代的用例😉。
我希望这对你有帮助,也很有趣!祝您好运!