React 中的 UI 元素被称为组件。组件定义了 UI 元素的外观(布局、样式、动画)和行为。一旦定义了组件,就可以将其集成到其他组件中以构建完整的用户界面。
React 组件派生自模板基类 React.Component<P, S>。P 和 S 指的是 props 和 state,这两个概念我们将在下面探讨。React 组件中最重要的方法是 render 方法。下面的示例展示了一个简单的 React 组件,它只渲染一些文本。
class HelloWorld extends React.Component<void, void> {
render() {
return <div>Hello World</div>;
}
}
此示例使用 JSX 尖括号语法。TypeScript 1.6 包含对这种语法的原生支持。只需将源文件命名为“tsx”而不是“ts”文件扩展名。
请注意,此组件正在发出一个“div”标签,这仅在浏览器环境中有效。要将其转换为 ReactXP 组件,只需将“div”替换为“RX.Text”标签。
class HelloWorld extends RX.Component<void, void> {
render() {
return <RX.Text>Hello World</RX.Text>;
}
}
另请注意,在上面的示例中,RX.Component
替换了 React.Component
。ReactXP 重新导出 React.Component
为 RX.Component
,这样您的导入将保持整洁,您无需专门导入 React
。
父组件通过指定参数来自定义子组件很方便。React 允许组件定义一组属性(或简称“props”)。一些 props 是必需的,另一些是可选的。props 可以是简单值、对象,甚至函数。
我们将修改 Hello World 示例以引入一个可选的“userName”prop。如果指定,组件将向用户渲染一个问候消息。组件类中的方法可以使用“this.props”访问 props。
interface HelloWorldProps {
userName?: string; // Question mark indicates prop is optional
}
class HelloWorld extends RX.Component<HelloWorldProps, void> {
render() {
return (
<RX.Text>
{ 'Hello ' + (this.props.userName || 'World') }
</RX.Text>
);
}
}
上面的示例使用默认样式(字体、大小、颜色等)渲染字符串。您可以通过指定“style”prop 来覆盖样式默认值。在此示例中,我们以绿色背景渲染粗体文本。请注意,React(和 ReactXP)中的样式大量借鉴了 CSS。
// By convention, styles are created statically and referenced
// through a private (not exported) _styles object.
const _styles = {
container: RX.Styles.createViewStyle({
backgroundColor: 'green'
}),
text: RX.Styles.createTextStyle({
color: 'red',
fontSize: 36, // Size in pixels
fontWeight: 'bold'
})
};
class HelloWorld extends RX.Component<void, void> {
render() {
return (
<RX.View style={ _styles.container }>
<RX.Text style={ _styles.text }>
Hello World
</RX.Text>
</RX.View>
);
}
}
有关样式属性的更多详细信息,请参阅样式文档或每个组件的文档。
React 使用 flexbox 指令进行组件布局。这些指令与样式信息一起指定。网上有许多 flexbox 教程。此处是我们特别推荐的一个。使用 flexbox 指令,您可以指定主要布局方向(行或列)、对齐、对齐方式和间距。
React 还借鉴了 CSS 中的外边距和内边距概念。外边距是组件周围的空间量,内边距是组件边界与其子组件之间的空间量。
这是一个结合了外边距、内边距和 flexbox 指令的样式示例。
const _styles = {
container: RX.Styles.createViewStyle({
flexDirection: 'column',
flexGrow: 1,
flexShrink: 1,
alignSelf: 'stretch',
justifyContent: 'center',
margin: 4,
padding: 4,
backgroundColor: 'green'
})
};
有关布局指令的更多详细信息,请参阅样式文档。
事件,例如用户手势、按键或鼠标操作,通过作为 props 指定的事件处理回调来报告。在此示例中,组件为按钮注册了一个 onPress 回调。
class CancelButton extends RX.Component<void, void> {
render() {
return (
<RX.Button onPress={ this._onPress }>
Cancel
</RX.Button>
);
}
private _onPress = (e: RX.SyntheticEvent) => {
e.stopPropagation();
// Cancelation logic goes here.
}
}
此示例使用 TypeScript lambda 函数在类创建时将 _onPress 变量绑定到方法实例。它还演示了一些约定(使用变量名“e”表示事件对象,以及以 _ 开头的方法名表示它是私有的)。它还演示了一个最佳实践(调用 stopPropagation 方法表示事件已处理)。
正如我们在上面的示例中看到的,组件的外观和行为可以根据外部提供的 props 发生变化。它还可以根据其内部管理的状态发生变化。作为一个简单的示例,当用户将鼠标悬停在组件上时,视觉样式可能会发生变化。
React 组件可以定义一个 state 对象。当此对象通过使用 setState 方法更新时,组件的 render 方法会自动调用。在下面的示例中,我们实现了一个简单的红绿灯,它有两种状态。根据当前状态,灯光被绘制成红色或绿色。按下或单击会切换状态。
interface StopLightState {
// Fields within a state object are usually defined as optional
// (hence the question mark below) because calls to setState
// typically update only a subset of the fields.
isStopped?: boolean;
}
const _styles = {
redButton: RX.Styles.createViewStyle({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'red'
}),
greenButton: RX.Styles.createViewStyle({
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: 'green'
})
};
class StopLight extends RX.Component<void, StopLightState> {
getInitialState(): StopLightState {
return { isStopped: true };
}
render() {
// Choose the appropriate style for the current state.
var buttonStyle = this.state.isStopped ?
_styles.redButton : _styles.greenButton;
return (
<RX.Button style={ buttonStyle }
onPress={ this._onToggleState } />
);
}
private _onToggleState = (e: RX.MouseEvent) => {
e.stopPropagation();
// Flip the value of "isStopped" and re-render.
this.setState({ isStopped: !this.state.isStopped });
}
}
组件状态也可以存储为类定义的实例变量。但是,如果 render 方法使用了数据片段,最好将其添加到 state 对象并通过调用 setState 进行更新。这样,渲染的组件将始终反映当前状态。