【问题标题】:React.createElement - converting jsx to normal function calls on a large scaleReact.createElement - 将 jsx 大规模转换为普通函数调用
【发布时间】:2019-04-19 09:51:21
【问题描述】:

我应该如何将 jsx 库和元素转换为正常的函数调用?以下两个代码示例之间是否有任何区别(带有 react-router 组件的示例简单应用程序组件只是一个示例,可以是任何东西,也可以是任何元素类型,如 'div'、'p' 或其他 jsx 库):

// normal jsx
const PublicApp = () => (
  <Switch>
    <Route path="/register" component={Register} />
    <Route path="/login" component={Login} />
    <Route path="/about" component={About} />
    <Redirect to="/login" />
  </Switch>
)
// desired api
const PublicApp = () =>
  Switch(
    {},
    Route({ path: '/register', component: Register }),
    Route({ path: '/login', component: Login }),
    Route({ path: '/about', component: About }),
    Redirect({ to: '/login' })
  )
// required helpers
import { createElement as h } from 'react'
import * as ReactRouter from 'react-router'

export const Switch = (props, ...children) =>
  h(ReactRouter.Switch, props, ...children)

export const Route = (props, ...children) =>
  h(ReactRouter.Route, props, ...children)

export const Redirect = (props, ...children) =>
  h(ReactRouter.Redirect, props, ...children)

export const Div = (props, ...children) => h('div', props, ...children)

. . . .

两者似乎都可以正常工作。大规模这样做有什么问题吗?此外,必须手动包装每个元素类型似乎有点愚蠢。有没有比使用这样的转换器更好的方法:

const converter = el => (props, ...children) => h(el, props, ...children)

export const Switch = converter(ReactRouter.Switch)

export const Route = converter(ReactRouter.Route)

export const Redirect = converter(ReactRouter.Redirect)

export const Div = converter('div')

【问题讨论】:

  • 如果你不想使用 JSX,为什么不直接调用React.createElement(...) 呢?如果你希望每个元素都有一个命名函数,为什么不直接使用 JSX?
  • @MTCoster 为什么不把番茄酱倒在你的意大利面上呢?我的意思是,它比那种精美的 marinara 酱更接近你……我不是 仇恨者,有时它会使代码看起来更干净。现在,无论出于何种原因,我只想使用干净的 javascript 并跳过所有花哨的废话。

标签: javascript reactjs


【解决方案1】:

我不确定这是否能回答您的问题,因为我什至不确定您的问题是否可以回答。

认为你想要的是一个 JavaScript 等价物,例如这个 Reagent(clojurescript React 包装库)组件:

(defn MyComponent [name]
  [:div.my-component
    [:p.my-component-content
      "Hello there "
      [:span.my-component-name 
        {:style {:color "red"}} 
        name]]])

对于那些不喜欢 cljs 的人来说,它是一个纯函数式 React 组件,只有一个 prop(名称),并渲染出一个带有 css 类“my-component”的 div 标签,其中包含带有 span 等的 ap 标签等。对比有了这个:

const MyComponent = ({name}) => (
  <div className="my-component">
    <p className="my-component-content">
      "Hello there " <span className="my-component-name" style="color:red;">name</span>
    </p>
  </div>
);

乍一看没有太大区别,除了...在 ClojureScript 版本中,所有这些声明性 html 位都是一流的常规 ClojureScript 数据结构。您可以使用所有常规语言结构和工具对它们进行内省,因为与 JSX 不同,它不是嵌入式 DSL。您可以通过添加 refs 从 JSX 中获得它,但这并不漂亮。

请注意,显式 React.createElement 版本没有这个问题,它一直是对象,并且您在词法范围内拥有引用,就像您对任何其他绑定一样。但是你失去了声明性:显式版本更加冗长(如果你想要好处,你必须将它用于所有事情)。

我不确定是否有办法在 JavaScript React 中获得蛋糕(声明式简洁)并吃掉它(一流的语言结构)。由于 Facebook 向一些非常有才华的工程师支付了很多钱来解决这个确切的问题,而且他们的解决方案是将 JSX 作为 DSL 嵌入,我猜不是。但如果可以的话,那就太好了。

最后一点。

小心偏离人迹罕至的地方太远。如果您做了一些太奇怪的事情,其他人(甚至可能是未来的您)会出现并查看您为开发人员人体工程学而做的一些奇怪的事情并说“我什至无法阅读这个热垃圾”。创新发生在那个领域,但很多灾难也是如此。

【讨论】:

  • 谢谢,这些天我好像经常在外太空!
  • @RichardForrester 好吧,“必须有更好的方法来做这个废话”是对 2018 年编程状态的完全理性的回应,“那么为什么没有人已经做到了呢? "是一个完全理性的反问。 :)
【解决方案2】:

经过一番搜索,njsx 似乎拥有最好的和经过深思熟虑的 api。

第三方库组件可以单独包装在 nsjx 默认导出中。

您甚至可以进行无点渲染(使用 ramda.compose 测试):

<Provider store={store}>
  <PersistGate loading={null} persistor={persistor}>
    <BrowserRouter>
      <Route path="/" component={App} />
    </BrowserRouter>
  </PersistGate>
</Provider>

变成:

compose(
  Provider({ store }),
  PersistGate({ loading: null, persistor }),
  BrowserRouter,
  Route
)({ path: '/', component: App })()

来自docs

无 JSX

一个基于纯函数的接口,用于创建没有 JSX 标签的 ReactReact Native 组件。

如果你喜欢 React 但不太喜欢嵌入的 HTML 标签,这个库可能就是你要找的。仅以干净、声明性的方式使用代码构建您的组件。

const myView = () =>
  div.app(
    div.header(
      img({src: logo, alt:'logo'}),
      h2('Welcome to NJSX')
    )
  )()

【讨论】:

    猜你喜欢
    • 2012-10-20
    • 1970-01-01
    • 2019-04-12
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 1970-01-01
    • 2022-12-06
    相关资源
    最近更新 更多