文档 / rescript-react / 元素 & JSX
Edit

元素 & JSX

React 元素是 React 应用的最小构件。本节将说明如何使用 ReScript 的 JSX 语法在 React 应用中操作 React.element

注意: 以下内容假设你的 bsconfig.json 拥有配置 "reason": { "react-jsx": 3 },否则你的 JSX 将不会转换为它的 React 特定形式。

元素基础

让我们从创建第一个React元素开始。

RES
let element = <h1> {React.string("Hello World")} </h1>

element 绑定和表达式 {React.string("Hello World")} 都是 React.element 类型,React.element 是 React 应用中表示 React 元素的基本类型。元素描述了你能在屏幕上看到的内容。

当你想创建一个函数来操作另一个 React 元素,例如 children ,你可以将 children 的类型标记为 React.element

RES
let wrapChildren = (children: React.element) => { <div> <h1> {React.string("Overview")} </h1> children </div> } wrapChildren(<div> {React.string("Let's use React with ReScript")} </div>)

理解 React.element 的定义非常重要,因为它在 React API 中被大量使用,例如 ReactDOM.render(element, ...) 等。需要注意的是,JSX 不会自动将 string 转换为 React.element (ReScript 强制要求显式类型转换)。例如, <div> Hello World </div> 将不会进行类型转换(这实际上是好事,因为它很容易引发 bug !),你需要使用 React.string 函数将 "Hello World" 显式转换为 React.element

我们的 React bindings 拥有所有的必要功能,用于将所有相关的数据类型表示为 React.element

使用 JSX 组合元素

你能用 JSX 将元素组合成更复杂的结构:

RES
let greeting = React.string("Hello ") let name = React.string("Stranger"); // element is also of type React.element let element = <div className="myElement"> greeting name </div>

JSX 能将你的 React 应用表示为一颗 React 元素构成的树。

当你与现有的 ReactJS 代码库进行大量的互操作时,可能会遇到语法限制而不能使用 JSX 语法,请查看逃生通道

创建元素

stringintfloatarray 创建元素

除了使用 JSX 来创建 React 元素或 React 组件,React 模块还提供了各种函数来从原始数据类型创建元素:

RES
React.string("Hello") // new element representing "Hello" React.int(1) // new element representing "1" React.float(1.0) // new element representing "1.0"

还提供了 React.array 来将多个元素表示为单个元素(对于渲染数据列表和传递子元素很有用):

RES
let element = React.array([ React.string("element 1"), React.string("element 2"), React.string("element 3") ])

注意: 我们不提供 React.list 函数,因为 list 值会增加运行时开销。 ReScript 关注干净、符合习惯的 JS 输出。如果你想把一个元素 list 转换成单个 React 元素,使用 Belt.List.toArray 转换为数组后再转换为 React.array

创建 Null 元素

由于 ReScript 的强类型特性,不允许 element || null 这样的约束。当你要根据值来控制元素的渲染时,你需要 React.null 常量来表示 Null

ReScriptJS Output
let name = Some("Andrea")

let element = switch name {
  | Some(name) => <div> {React.string("Hello " ++ name)} </div>
  | None => React.null
}

<div> element </div>

逃生通道

注意: 本章节介绍了 JSX 使用的底层 API ,只有在遇到 JSX 语法限制时才应使用。关于 JSX 内部实现的更多信息可参考超越 JSX

用组件函数创建元素

注意:组件和 props 的细节会在下一章说明。

有时候需要传递组件函数来更好地控制 React.element 的创建。使用 React.createElement 函数来实例化你的元素:

RES
type props = {"name": string}; let render = (myComp: props => React.element) => { <div> {React.createElement(myComp, {"name": "Franz"})} </div> }

通常在与现有的 JS/ReactJS 代码交互时使用这个特性。在纯 ReScript React 应用中,你会传递一个渲染函数(也叫 “render prop”):

RES
let render = (renderMyComp: (~name: string) => React.element) => { <div> {renderMyComp(~name="Franz")} </div> }

传递任意多个子元素

还有一个 React.createElementVariadic 函数,它接受一个子元素数组作为第三个参数:

ReScriptJS Output
type props = {"title": string, "children": React.element};

let render = (article: props => React.element) => {
  let children = [React.string("Introduction"), React.string("Body")];

  let props = {"title": "Article #1", "children": React.null};

  {React.createElementVariadic(article, props, children)}
}

注意: 这里我们传递了一个 prop "children": React.null 来满足类型检查。 React 会忽略 children prop 而会使用 children 数组。

这个函数主要被 JSX 转换使用,通常你需要使用 React.createElement 并传递一个 children prop。

创建DOM元素

要创建 DOM 元素(<div><span> 等),使用 ReactDOMRe.createDOMElementVariadic

RES
ReactDOMRe.createDOMElementVariadic("div", ~props=ReactDOM.domProps(~className="card", ()), []);

上面的函数需要 ReactDOM.domProps 构造函数,这样 ReScript 可以确保我们只传递有效的 DOM props。你可以在 ReactDOM 模块中找到所有可用的 props。

警告: ReactDOMRe 模块会在下个主要的 rescript-react 版本中被移除,所以尽量少用!

复制元素

注意: 这是一个逃生通道特性,只有在与现有的 JS 代码/库进行交互时才有用。

有时需要复制一个现有元素,并对新实例设置、覆写或添加 props,或者你想要设置无效的 prop 名称,例如 data-name 。你可以使用 React.cloneElement

ReScriptJS Output
let original = <div className="hello"/>

// Will return a new React.element with className set to "world"
React.cloneElement(original, {"className": "world", "data-name": "some name"});

上面提到的特性也可以模仿 props 传播,一个在 ReactJS 代码库中常见的用法,但由于它的不安全性和不正确性,我们非常不推荐你这样做(例如,给组件添加未定义的额外 props 是没有意义的,并会导致难以发现的 bug )。

在 ReScript 中,我们会将需要的 props 显式传递给叶子组件,或者使用 renderProp 。我们引入了JSX 双关语法来更便捷地传递 props。