Kev*_*ker 4 javascript monads functional-programming react-jsx ramda.js
我的React应用程序代码库中重复了以下模式:
const {items, loading} = this.props
const elem = loading
? <Spinner />
: items.length
? <ListComponent />
: <NoResults />
Run Code Online (Sandbox Code Playgroud)
尽管这肯定比嵌套实际 if/else子句更干净,但我正在尝试拥抱更优雅和实用的模式。我已经读过有关使用Eithermonad之类的东西的信息,但是我为之付出的所有努力最终看起来都更加冗长,并且可重用性较低(鉴于我想记住以前的尝试,该伪代码可能无法正常工作) :
import {either, F, isEmpty, prop} from 'ramda'
const isLoading = prop('loading')
const renderLoading = (props) => isLoading(props) ? <Spinner /> : false
const loadingOrOther = either(renderLoading, F)
const renderItems = (props) => isEmpty(props.items) ? <NoResults /> : <ListComponent />
const renderElem = either(loadingOrOther, renderItems)
const elems = renderElem(props)
Run Code Online (Sandbox Code Playgroud)
我可以使用哪种模式更干燥/可重用?
谢谢!
虽然这肯定比嵌套实际
if/else子句更干净Run Code Online (Sandbox Code Playgroud)render () { const {items, loading} = this.props return loading ? <Spinner /> : items.length ? <ListComponent items={items} /> : <NoResults /> }
您已发布了不完整的代码,因此,我为一些更具体的示例填补了一些空白。
查看您的代码,我很难理解条件在哪里,返回值在哪里。条件分散在不同缩进级别的不同行上–同样,返回值也没有视觉一致性。实际上,直到您进一步阅读程序以查看时,loadingin return loading才是条件?。选择哪个组件在这种情况下渲染是一个平坦的决定,而你的代码的结构应该反映。
使用这里if/else产生了一个非常易读的示例。没有嵌套,您可以看到返回的各种类型的组件,它们整齐地放置在它们对应的return语句旁边。这是一个简单而又简单的决定,并带有简单,详尽的案例分析。
我在此强调详尽的一词,因为重要的是您至少要提供决策分支if和else选择分支。在您的情况下,我们有第三种选择,因此else if采用了一种。
render () {
const {items, loading} = this.props
if (loading)
return <Spinner />
else if (items.length)
return <ListComponent items={items} />
else
return <NoResults />
}
Run Code Online (Sandbox Code Playgroud)
如果您正在查看此代码并试图“修复”它,因为您正在考虑“拥抱更加优雅和实用的模式”,则会误解“优雅”和“功能”。
嵌套三元表达式没有什么优雅之处。函数式编程与编写具有最少按键次数的程序无关,这会导致程序过于简洁且难以阅读。
if/else我使用的语句在某种程度上也没有“功能性”,因为它们涉及不同的语法。当然,它们比三元表达式更冗长,但是它们恰好按照我们期望的方式运行,并且它们仍然允许我们声明功能性行为 –不要让语法本身迫使您对编码风格做出愚蠢的决定。
我同意不幸的是,这if是JavaScript中的语句,而不是表达式,但这就是您所需要的。在这样的约束下,您仍然能够生成精美且功能强大的程序。
备注
我个人认为,依靠真实的价值观是很重要的。我宁愿将您的代码写为
render () {
const {items, loading} = this.props
if (loading) // most important check
return <Spinner />
else if (items.length === 0) // check of next importance
return <NoResults />
else // otherwise, everything is OK to render normally
return <ListComponent items={items} />
}
Run Code Online (Sandbox Code Playgroud)
与您的代码相比,这更不可能吞下错误。例如,假装您的组件以某种方式具有prop值loading={false} items={null}–您可以说您的代码将优雅地显示该NoResults组件;我认为您的组件处于非加载状态且没有项目是一个错误,而我的代码将产生一个错误来反映这一点:Cannot read property 'length' of null。
这向我表明,更大的问题正在发生在此组件范围之外的某个地方–即,该组件具有loading = true 或某些项目数组(空或其他);其他道具组合均不可接受。
我认为你的问题并不是关于 if 语句与三元表达式。我认为您可能正在寻找不同的数据结构,允许您以强大的 DRY 方式抽象条件。
有一些数据类型可以在抽象条件时派上用场。例如,您可以使用Any, 或Allmonoid 来抽象相关条件。您可以使用Either, 或Maybe。
您还可以查看 Ramda 的cond、when和 等函数ifElse。您已经了解了总和类型。这些都是在特定情况下强大且有用的策略。
但根据我的经验,这些策略确实在人们的视野之外大放异彩。在视图中,我们实际上想要可视化层次结构,以便了解它们将如何呈现。所以三元是一个很好的方法来做到这一点。
人们可能不同意“功能性”的含义。有些人说函数式编程是关于纯粹性或引用透明性;其他人可能会说这只是“用函数编程”。不同的社区有不同的解释。
因为 FP 对于不同的人来说意味着不同的事情,所以我将重点关注一个特定的属性,即声明性代码。
声明性代码在一处定义算法或值,并且不会强制地在单独的部分中进行更改或变异。声明性代码说明某物是什么,而不是通过不同的代码路径强制为名称分配值。您的代码当前是声明性的,这很好!声明性代码提供了保证:例如“这个函数肯定会返回,因为该return语句位于第一行”。
有一种错误的观念认为三元数是嵌套的,而 if 语句是平面的。这只是一个格式问题。
return (
condition1
? result1
: condition2
? result2
: condition3
? result3
: otherwise
)
Run Code Online (Sandbox Code Playgroud)
将条件放在自己的行上,然后嵌套响应。您可以根据需要多次重复此操作。最后的“else”就像任何其他结果一样缩进,但它没有条件。它可以扩展到您想要的任意数量的案例。我已经看到并编写了许多像这样的平面三元组的视图,并且我发现更容易准确地遵循代码,因为路径没有分开。
你可能会说if语句更具可读性,但我再次认为可读性对于不同的人来说意味着不同的事情。因此,为了解开这一点,让我们思考一下我们所强调的是什么。
当我们使用三元数时,我们强调只有一种可能的方式来声明或返回某些内容。如果函数仅包含表达式,则我们的代码更有可能被视为公式,而不是公式的实现。
当我们使用 if 语句时,我们强调的是单独的、分离的步骤来产生输出。如果您希望将自己的观点视为单独的步骤,那么 if 语句是有意义的。如果您希望将视图视为具有基于上下文的不同表示的单个实体,那么三元组和声明性代码会更好。
总之,您的代码已经可以运行了。可读性和易读性是主观的,专注于你想要强调的内容。不要觉得表达式中的多个条件是代码味道,它只是代表了 UI 的复杂性,解决这个问题的唯一方法(如果需要解决)就是更改 UI 的设计。UI 代码可以很复杂,并且让您的代码诚实并代表其所有潜在状态并没有什么可耻的。
| 归档时间: |
|
| 查看次数: |
2729 次 |
| 最近记录: |