Intro to Storybook
  • !ابدأ
  • مكون بسيط
  • مكون مركب
  • البياتات
  • واجهات
  • أطلق
  • الاختبار
  • الإضافات
  • الخلاصة
  • ساهم
Framework:
ReactReact NativeVueAngularSvelteEmber

اربط البيانات

تعلم كيفية ربط البيانات مع مكون واجهة المستخدم
هذه الترجمة المجتمعية لم يتم تحديثها إلى اخر اصدار من ستوريبوك بعد. ساعدنا في تحديثها عن طريق اتباع التغييرات في دليل العربية في هذه الترجمة طلبات Pull مرحب بها.

أنشأنا حتى الأن مكونات بدون حالة والتي تعتبر مناسبة لستوريبوك ولكن ليست ذات جدوى إلا إذا أعطيناها بعض من البيانات في تطبيقنا

هذا الدرس لا يركز على تفاصيل بناء التطبيق لذلك لن نتطرق لهذه التفاصيل. ولكن سنتوقف لحظة لنلقي نظرة على الأنماط المتداولة عند ربط البيانات مع المكونات الحاوية.

المكونات الحاوية

مكون TaskList خاصتنا مكتوب في صورة مظهرية (راجع منشور هذه المدونة) أي انه لا يتصل مع أي شيء خارج محيط تنفيذه. لتمرير البيانات إليه, نحتاج إلى "حاوية".

هذا المثال يستخدم ريدكس, أشهر مكتبة رياكت لتخزين البيانات, لبناء نموذج بيانات بسيط لتطبيقنا. ولكن هذا النمط المستخدم يمكن تطبيقه على أي مكتبة إدارة بيانات أخرى مثل أبولو وموب اكس.

أضف التبعيات الضرورية لمشروعك عن طريق:

yarn add react-redux redux

سنبني أولا مخزن ريدكس يستجيب لأحداث تبدل في حالة المهام خاصتنا, في ملف تحت اسم lib/redux.js في مجلد src (أٌبقى بسيط عن قصد):

src/lib/redux.js
// A simple redux store/actions/reducer implementation.
// A true app would be more complex and separated into different files.
import { createStore } from 'redux';

// The actions are the "names" of the changes that can happen to the store
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 });

// 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
export const reducer = (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;
  }
};

// The initial state of our store when the app loads.
// Usually you would fetch this from a server
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' },
];

// We export the constructed redux store
export default createStore(reducer, { tasks: defaultTasks });

سنغير بعدها في التصديرة الافتراضية من مكون TaskList ليتصل مع مخزن ريدكس ويٌظهر المهام التي نحن مهتمين بهم:

src/components/TaskList.js
import React from 'react';
import PropTypes from 'prop-types';

import Task from './Task';

import { connect } from 'react-redux';
import { archiveTask, pinTask } from '../lib/redux';

export function PureTaskList({ loading, tasks, onPinTask, onArchiveTask }) {
  /* previous implementation of TaskList */
}

PureTaskList.propTypes = {
  /** Checks if it's in loading state */
  loading: PropTypes.bool,
  /** The list of tasks */
  tasks: PropTypes.arrayOf(Task.propTypes.task).isRequired,
  /** Event to change the task to pinned */
  onPinTask: PropTypes.func.isRequired,
  /** Event to change the task to archived */
  onArchiveTask: PropTypes.func.isRequired,
};

PureTaskList.defaultProps = {
  loading: false,
};

export default connect(
  ({ tasks }) => ({
    tasks: tasks.filter(t => t.state === 'TASK_INBOX' || t.state === 'TASK_PINNED'),
  }),
  dispatch => ({
    onArchiveTask: id => dispatch(archiveTask(id)),
    onPinTask: id => dispatch(pinTask(id)),
  })
)(PureTaskList);

بما أن لدينا بيانات حقيقية مأخوذة من ريدكس في مكوننا, يمكننا ربطه مع src/app.js وإظهار المكون هناك. ولكن لنتأخر عن ذلك ونستمر في رحلتنا المبنية عن المكون.

لا تقلق سنتعامل مع ذلك في الفصل التالي.

في هذه المرحلة, ستتوقف اختبارات ستوريبوك عن العمل لأن TaskList أصبح حاوية ولا يقبل خاصيات بعد الآن. تقوم TaskList عوضا عن ذلك بالاتصال مع المخزن وتقوم بوضع الخاصيات في مكون PureTaskList المحيطة به.

و لكن يمكننا حل هذه المشكلة بكل بساطة عن طريق عرض PureTaskList -- المكون المظهري الذي أضفناه لتونا في جملة export في الخطوة السابقة -- في ستوريز الخاصة بستوريبوك:

src/components/TaskList.stories.js
import React from 'react';

+ import { PureTaskList } from './TaskList';
import * as TaskStories from './Task.stories';

export default {
+ component: PureTaskList,
  title: 'TaskList',
  decorators: [story => <div style={{ padding: '3rem' }}>{story()}</div>],
};

+ const Template = args => <PureTaskList {...args} />;

export const Default = Template.bind({});
Default.args = {
  // Shaping the stories through args composition.
  // The data was inherited the Default story in task.stories.js.
  tasks: [
    { ...TaskStories.Default.args.task, id: '1', title: 'Task 1' },
    { ...TaskStories.Default.args.task, id: '2', title: 'Task 2' },
    { ...TaskStories.Default.args.task, id: '3', title: 'Task 3' },
    { ...TaskStories.Default.args.task, id: '4', title: 'Task 4' },
    { ...TaskStories.Default.args.task, id: '5', title: 'Task 5' },
    { ...TaskStories.Default.args.task, id: '6', title: 'Task 6' },
  ],
};

export const WithPinnedTasks = Template.bind({});
WithPinnedTasks.args = {
  // Shaping the stories through args composition.
  // Inherited data coming from the Default story.
  tasks: [
    ...Default.args.tasks.slice(0, 5),
    { id: '6', title: 'Task 6 (pinned)', state: 'TASK_PINNED' },
  ],
};

export const Loading = Template.bind({});
Loading.args = {
  tasks: [],
  loading: true,
};

export const Empty = Template.bind({});
Empty.args = {
  // Shaping the stories through args composition.
  // Inherited data coming from the Loading story.
  ...Loading.args,
  loading: false,
};
💡 مع هذا التغيير ستحتاج اللمحات خاصتك إلى تحديث, قم بإعادة تنفيذ أمر الاختبار عن طريق المؤشر -u لتحديثهم, ولا تنسى أيضا تنفيذ التغييرات إلى git
Keep your code in sync with this chapter. View 94b134e on GitHub.
Is this free guide helping you? Tweet to give kudos and help other devs find it.
Next Chapter
واجهات
أنشئ واجهة من المكونات
✍️ Edit on GitHub – PRs welcome!
Docs
Documentation
Add Storybook to your project in less than a minute to build components faster and easier.
reactvueangularweb-components
Tutorial
Tutorials
Learn Storybook with in-depth tutorials that teaches Storybook best practices. Follow along with code samples.
Learn Storybook now
Storybook
The MIT License (MIT). Website design by @domyen and the awesome Storybook community.
StorybookShowcaseDocsTutorialsAddonsBlogReleasesGet involvedUse casesSupportTelemetryTeam
Community GitHub Twitter Discord chat Youtube Component Driven UIs
Subscribe
Get news, free tutorials, and Storybook tips emailed to you.

Maintained by
Chromatic
Continuous integration by
CircleCI
Hosting by
Netlify