构建一个简单的组件
我们将遵循“组件驱动开发”(CDD) 方法论来构建我们的 UI。这是一种从“底层”开始构建 UI 的过程,从组件开始,以屏幕结束。CDD 有助于您在构建 UI 时应对日益复杂的挑战。
任务

Task是我们应用的核心组件。每个任务根据其所处的确切状态显示略有不同。我们显示一个勾选(或未勾选)的复选框、有关任务的一些信息,以及一个“固定”按钮,允许我们将任务在列表中上下移动。将这些组合在一起,我们将需要这些 props
title– 描述任务的字符串state- 任务当前在哪个列表中,是否已勾选?
当我们开始构建 Task 时,我们首先编写对应于上面草图的不同任务类型的测试状态。然后,我们使用 Storybook 使用模拟数据在隔离环境中构建组件。我们将逐步“视觉测试”组件在每种状态下的外观。
这个过程类似于我们可以称之为“视觉 TDD”的测试驱动开发 (TDD)。
准备就绪
让我们打开 .storybook/main.js 看看
module.exports = {
stories: ['../components/**/*.stories.?(ts|tsx|js|jsx)'],
addons: [
'@storybook/addon-ondevice-controls',
'@storybook/addon-ondevice-actions',
],
};
如果您查看 stories 属性,您会发现 Storybook 正在 components 文件夹中查找 stories。
在 React Native 的 Storybook 中,由于 Metro 打包器当前存在限制,我们依赖 main.js 中的配置来生成一个名为 storybook.requires 的文件,该文件用于加载我们项目中的所有 stories 和插件。当您运行 yarn storybook 启动您的 Storybook 时,此文件会自动生成。
💡 您也可以通过运行 yarn storybook-generate 手动生成 storybook.requires 文件。但是,除非您发现某个 story 未加载或发现您 main.js 配置中的更改未在您的 Storybook 中反映出来,否则您不需要重新创建此文件。要了解有关 storybook.requires 文件如何生成的更多信息,您可以查看 metro.config.js 文件中的 withStorybook 函数。
现在让我们创建任务组件及其配套的 story 文件:components/Task.jsx 和 components/Task.stories.jsx。
我们将从 Task 的基本实现开始,只需接收我们知道需要使用的属性以及您可以在任务上执行的两个操作(在列表之间移动它们)
import { StyleSheet, TextInput, View } from 'react-native';
import { styles } from './styles';
export const Task = ({
task: { id, title, state },
onArchiveTask,
onPinTask,
}) => {
return (
<View style={styles.listItem}>
<TextInput value={title} editable={false} />
</View>
);
};
现在添加 story 文件
import { Task } from './Task';
export default {
title: 'Task',
component: Task,
argTypes: {
onPinTask: { action: 'onPinTask' },
onArchiveTask: { action: 'onArchiveTask' },
},
};
export const Default = {
args: {
task: {
id: '1',
title: 'Test Task',
state: 'TASK_INBOX',
},
},
};
export const Pinned = {
args: { task: { ...Default.args.task, state: 'TASK_PINNED' } },
};
export const Archived = {
args: { task: { ...Default.args.task, state: 'TASK_ARCHIVED' } },
};
在我们的 stories 文件中,我们使用一种称为组件故事格式 (CSF) 的语法。这种语法使得编写 stories 更加容易,并且受最新版本 Storybook 的支持。
Storybook 有两个基本的组织级别:组件及其子故事。将每个故事视为组件的排列。你可以根据需要为每个组件创建任意数量的故事。
- 组件
- 故事
- 故事
- 故事
为了让 Storybook 了解我们正在记录的组件,我们创建一个 default 导出,其中包含:
component-- 组件本身title-- 如何在 Storybook 应用侧边栏中引用该组件argTypes-- 允许我们指定 args 的类型,在这里我们使用它来定义每次交互发生时都会记录的操作
为了定义我们的 stories,我们导出一个带有 args 属性的对象。参数或简称为 args ,使我们能够使用 controls 插件实时编辑我们的组件,而无需重新启动 Storybook。一旦 args 值发生更改,组件也会随之更改。
创建 story 时,我们使用基础的 task arg 来构建组件期望的任务形状。通常模仿实际数据外观的模型。同样, export 此形状将使我们能够在后续 stories 中重用它,正如我们将看到的。
现在我们已经设置了基础知识,让我们重新运行 yarn storybook 并查看我们的更改。如果您已经运行了 Storybook,您也可以运行 yarn storybook-generate 来重新生成 storybook.requires 文件。
您应该会看到如下 UI: 
您可以使用屏幕底部的菜单打开导航菜单,然后点击切换 stories。然后您可以点击其他地方或向下滑动以关闭菜单。您可以通过按下底部栏最右侧的图标找到插件。
构建状态
现在我们可以开始构建我们的组件以匹配设计了。
目前该组件仍然很简单。首先编写实现设计的代码,但不必过于详细
import { MaterialIcons } from '@expo/vector-icons';
import { StyleSheet, TextInput, TouchableOpacity, View } from 'react-native';
import { styles } from './styles';
export const Task = ({
task: { id, title, state },
onArchiveTask,
onPinTask,
}) => {
return (
<View style={styles.listItem}>
<TouchableOpacity onPress={() => onArchiveTask(id)}>
{state !== "TASK_ARCHIVED" ? (
<MaterialIcons
name="check-box-outline-blank"
size={24}
color="#26c6da"
/>
) : (
<MaterialIcons name="check-box" size={24} color="#26c6da" />
)}
</TouchableOpacity>
<TextInput
placeholder="Input Title"
value={title}
editable={false}
style={
state === "TASK_ARCHIVED"
? styles.listItemInputTaskArchived
: styles.listItemInputTask
}
/>
<TouchableOpacity onPress={() => onPinTask(id)}>
<MaterialIcons
name="star"
size={24}
color={state !== "TASK_PINNED" ? "#eee" : "#26c6da"}
/>
</TouchableOpacity>
</View>
);
};
完成后,它应该看起来像这样:

组件构建完成!
我们现在已经成功构建了一个组件,而无需服务器或运行整个应用程序。下一步是逐个以类似的方式构建其余的 Taskbox 组件。
正如你所见,在隔离环境中开始构建组件既简单又快速。由于我们可以深入测试每一种可能的状态,因此我们可以生产出质量更高、错误更少、更完善的 UI。