元素 & JSX
React 元素是 React 应用的最小构件。本节将说明如何使用 ReScript 的 JSX 语法在 React 应用中操作 React.element
。
注意: 以下内容假设你的
bsconfig.json
拥有配置"reason": { "react-jsx": 3 }
,否则你的 JSX 将不会转换为它的 React 特定形式。
元素基础
让我们从创建第一个React元素开始。
RESlet element = <h1> {React.string("Hello World")} </h1>
element
绑定和表达式 {React.string("Hello World")}
都是 React.element
类型,React.element
是 React 应用中表示 React 元素的基本类型。元素描述了你能在屏幕上看到的内容。
当你想创建一个函数来操作另一个 React 元素,例如 children
,你可以将 children
的类型标记为 React.element
:
RESlet 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 将元素组合成更复杂的结构:
RESlet 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 语法,请查看逃生通道。
创建元素
从 string
, int
, float
, array
创建元素
除了使用 JSX 来创建 React 元素或 React 组件,React
模块还提供了各种函数来从原始数据类型创建元素:
RESReact.string("Hello") // new element representing "Hello"
React.int(1) // new element representing "1"
React.float(1.0) // new element representing "1.0"
还提供了 React.array
来将多个元素表示为单个元素(对于渲染数据列表和传递子元素很有用):
RESlet 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
逃生通道
注意: 本章节介绍了 JSX 使用的底层 API ,只有在遇到 JSX 语法限制时才应使用。关于 JSX 内部实现的更多信息可参考超越 JSX。
用组件函数创建元素
注意:组件和 props 的细节会在下一章说明。
有时候需要传递组件函数来更好地控制 React.element
的创建。使用 React.createElement
函数来实例化你的元素:
REStype props = {"name": string};
let render = (myComp: props => React.element) => {
<div>
{React.createElement(myComp, {"name": "Franz"})}
</div>
}
通常在与现有的 JS/ReactJS 代码交互时使用这个特性。在纯 ReScript React 应用中,你会传递一个渲染函数(也叫 “render prop”):
RESlet render = (renderMyComp: (~name: string) => React.element) => {
<div> {renderMyComp(~name="Franz")} </div>
}
传递任意多个子元素
还有一个 React.createElementVariadic
函数,它接受一个子元素数组作为第三个参数:
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
:
RESReactDOMRe.createDOMElementVariadic("div", ~props=ReactDOM.domProps(~className="card", ()), []);
上面的函数需要 ReactDOM.domProps
构造函数,这样 ReScript 可以确保我们只传递有效的 DOM props。你可以在 ReactDOM 模块中找到所有可用的 props。
警告: ReactDOMRe
模块会在下个主要的 rescript-react
版本中被移除,所以尽量少用!
复制元素
注意: 这是一个逃生通道特性,只有在与现有的 JS 代码/库进行交互时才有用。
有时需要复制一个现有元素,并对新实例设置、覆写或添加 props,或者你想要设置无效的 prop 名称,例如 data-name
。你可以使用 React.cloneElement
:
上面提到的特性也可以模仿 props 传播
,一个在 ReactJS 代码库中常见的用法,但由于它的不安全性和不正确性,我们非常不推荐你这样做(例如,给组件添加未定义的额外 props 是没有意义的,并会导致难以发现的 bug )。
在 ReScript 中,我们会将需要的 props 显式传递给叶子组件,或者使用 renderProp 。我们引入了JSX 双关语法来更便捷地传递 props。