返回Storybook 简介
章节
  • 开始
  • 简单组件
  • 复合组件
  • 数据
  • 屏幕
  • 部署
  • 测试
  • 插件
  • 结论
  • 贡献

连接数据

了解如何将数据连接到 UI 组件
此社区翻译尚未更新到最新版本的 Storybook。请通过将英文指南中的更改应用于此翻译来帮助我们更新。 欢迎提交 Pull Request.

到目前为止,我们创建了隔离的无状态组件——这对 Storybook 来说很棒,但除非我们在应用程序中为它们提供一些数据,否则最终是没有用的。

本教程不关注构建应用程序的细节,因此我们不会在此处深入探讨这些细节。但是,我们将花一点时间来了解使用容器组件连接数据的常见模式。

加载数据

使用 Ember,您可以使用多种方法加载数据,但有两件事您需要记住

  • 数据加载的位置
  • 数据加载的方式

默认情况下,您应该使用路由和数据持久层,例如 ember-dataApollo。对于我们的小示例,我们将使用 tracked-redux 来演示如何将其与路由一起使用。

使用以下命令将必要的依赖项添加到您的项目中

复制
ember install tracked-redux

首先,我们将构建一个简单的 Redux 存储,它响应更改任务状态的操作,在 app 文件夹中名为 redux.js 的文件中(有意保持简单)

复制
app/store.js
import { createStore } from 'tracked-redux';

export const actions = {
  ARCHIVE_TASK: 'ARCHIVE_TASK',
  PIN_TASK: 'PIN_TASK',
};

// The action creators bundle actions with the data required to execute them
export const archiveTask = id => ({ type: actions.ARCHIVE_TASK, id });
export const pinTask = id => ({ type: actions.PIN_TASK, id });

// A sample set of tasks
const defaultTasks = [
  { id: '1', title: 'Something', state: 'TASK_INBOX' },
  { id: '2', title: 'Something more', state: 'TASK_INBOX' },
  { id: '3', title: 'Something else', state: 'TASK_INBOX' },
  { id: '4', title: 'Something again', state: 'TASK_INBOX' },
];

// Store's initial state
const initialState = {
  isError: false,
  isLoading: false,
  tasks: defaultTasks,
};

// All our reducers simply change the state of a single task.
function taskStateReducer(taskState) {
  return (state, action) => {
    return {
      ...state,
      tasks: state.tasks.map(task =>
        task.id === action.id ? { ...task, state: taskState } : task
      ),
    };
  };
}

// The reducer describes how the contents of the store change for each action
const reducers = (state, action) => {
  switch (action.type) {
    case actions.ARCHIVE_TASK:
      return taskStateReducer('TASK_ARCHIVED')(state, action);
    case actions.PIN_TASK:
      return taskStateReducer('TASK_PINNED')(state, action);
    default:
      return state || initialState;
  }
};

export const store = createStore(reducers);

使用路由

我们已经设置好了 store。现在我们可以在需要的对象上声明字段。

为此,我们将同时使用 routecontroller。后者将包含我们之前创建的操作,以便我们可以轻松修改我们的 store。

app 目录中,创建一个名为 tasks 的新目录,并在其中添加一个名为 route.js 的新文件,内容如下

复制
app/tasks/route.js
import Route from '@ember/routing/route';
import { store } from '../store';

export default class TasksRoute extends Route {
  model() {
    // returns the store tracked state
    // whenever the state changes, these will be reflected in the template
    return store
      .getState()
      .tasks.filter(t => t.state === 'TASK_INBOX' || t.state === 'TASK_PINNED');
  }
}

接下来,我们需要 controller。在 tasks 文件夹中,创建另一个名为 controller.js 的文件,内容如下

复制
app/tasks/controller.js
import Controller from '@ember/controller';
import { action } from '@ember/object';

import { store, pinTask, archiveTask } from '../store';

export default class TaskController extends Controller {
  @action
  pinTask(task) {
    store.dispatch(pinTask(task));
  }

  @action
  archiveTask(task) {
    store.dispatch(archiveTask(task));
  }
}

最后一个文件名为 template.hbs,在其中我们将添加在 上一章 中创建的展示型 <TaskList> 组件

复制
app/tasks/template.hbs
<TaskList
  @tasks={{@model}}
  @pinTask={{this.pinTask}}
  @archiveTask={{this.archiveTask}}
 />

通过这样做,我们已经完成了我们想要做的事情,我们已经设法建立了一个数据持久层,并且通过采用一些最佳实践,我们还设法保持组件的解耦。

我们的实现相当基础,如果我们决定更新我们的应用程序,则需要额外的工作。在下一章中,我们将介绍屏幕组件,这将改进我们的小型应用程序中数据处理的方式。

💡 别忘了使用 git 提交你的更改!
这个免费指南对您有帮助吗?发推文以示赞赏并帮助其他开发人员找到它。
下一章
屏幕
用组件构建屏幕
✍️ 在 GitHub 上编辑 – 欢迎提交 PR!
加入社区
6,721位开发者及更多
为什么为什么选择 Storybook组件驱动的 UI
开源软件
Storybook - Storybook 中文

特别感谢 Netlify CircleCI