ساخت کتابخانه React و انتشار آن در NPM — راهنمای گام به گام

۴۸۹ بازدید
آخرین به‌روزرسانی: ۱۹ شهریور ۱۴۰۲
زمان مطالعه: ۶ دقیقه
دانلود PDF مقاله
ساخت کتابخانه React و انتشار آن در NPM — راهنمای گام به گام

اغلب ما در طی عمر کاری خود مشغول توسعه اپلیکیشن هستیم و ساخت کتابخانه React تا حدودی دور از دسترس به نظر می‌رسد. اما در عمل چنین نیست. انتشار یک کامپوننت ری‌اکت روی NPM به‌طوری که توسعه‌دهندگان دیگر بتوانند از آن استفاده کنند، فرایند ساده‌ای است و می‌تواند بسیار الهام‌بخش باشد. بدین ترتیب می‌توانیم مهارت‌های ری‌اکت خود را ارتقا ببخشیم و با جامعه توسعه‌دهندگان آن همگام شویم.

فهرست مطالب این نوشته
997696

در این راهنما قصد داریم یک کامپوننت نمونه به شکل یک ویجت ساده مدیوم توسعه دهیم که روی NPM منتشر خواهد شد. این کامپوننت یک نام کاربری به عنوان prop می‌گیرد و سپس آخرین فعالیت کاربر را واکشی می‌کند. مقالات و نظرات کاربر نیز در یک modal نمایش می‌یابند.

ساخت کتابخانه React

می‌توانید این ویجت را در وب‌سایت خود قرار دهید و مقالات مدیوم خود را به وسیله آن نمایش دهید. در این لینک (+) می‌توانید دمویی از این ویجت را ببینید و با وارد کردن نام کاربری خود در وب‌سایت مدیوم با طرز کار آن آشنا شوید.

ساخت ویجت

به لطف اسکریپت create-react-library (+) اغلب مراحل ساخت خودکار شده است. در ادامه به صورت گام به گام یک کامپوننت ری‌اکت به نام <MediumProfile /> می‌سازیم.

ابتدا create-react-library را به صورت سراسری نصب کنید:

npm install -g create-react-library

کتابخانه را مقداردهی کنید:

create-react-library

از شما خواسته می‌شود که جزییات زیر را وارد کنید:

اکنون همه چیز پکیج شده و آماده به کار است.

  • گام 1: پوشه به پروژه cd کرده و دستور npm start را اجرا کنید. بدین ترتیب ماژول ‎/src مورد نظارت قرار گرفته و هر زمان که تغییری در آن ایجاد شود در dist/ مجدداً کامپایل می‌شود.
  • گام 2: خط فرمان دوم را باز کنید به پوشه ‎/example بروید و دستور npm start را اجرا کنید.

اکنون یک سرور زنده داریم که تغییرات را در src و example مورد نظارت قرار داده و به صورت آنی بارگذاری مجدد می‌شود. از این رو می‌توانیم به سادگی کامپوننت خود را توسعه دهیم. مراحل کار به صورت زیر است:

ساخت کتابخانه React

دو فایل اصلی وجود دارد که با آن‌ها کار خواهیم کرد:

  1. src/index.js
  2. example/src/App.js

فایل اول یعنی src/index.js کامپوننت اکسپورت شده ما است که از سوی توسعه‌دهندگان دیگر مورد استفاده قرار می‌گیرد. این فایل شامل یک کامپوننت است که prop-ها را از فرم می‌گیرد و نوعی نتیجه در اختیار کاربر قرار می‌دهد.

توجه داشته باشید که ما نوع prop-های دریافتی را بررسی می‌کنیم. برای نمونه به کاربر اجازه می‌دهیم که prop به نام size را ارسال کند تا اندازه دکمه مدیوم را تغییر دهد. این بدان معنی است که باید size: Proptypes.number را به پروتوتایپ‌های خود اضافه کنیم.

ساخت کتابخانه React

فایل دوم یعنی example/src/App.js یک اپلیکیشن نمونه است که کامپوننت ما را ایمپورت می‌کند. بدین ترتیب props کامپوننت ارائه شده و نتیجه به دست می‌آید. نتیجه در مرورگر به صورت زیر است:

ساخت کتابخانه React

اکنون که دو فایل را در اختیار داریم، باید شروع به ساختن کامپوننت src/index.js بکنیم. کامپوننت ما یک کلمه به کاربر نشان می‌دهد که می‌تواند روی آن کلیک کنید تا فعالیتش در وب‌سایت مدیوم واکشی شود.

زمانی که کاربر کلیک کرد یک Modal باز می‌شود و داده‌های واکشی شده کاربر را نمایش می‌دهد. چند بررسی خطا و حالت بارگذاری اضافه می‌کنیم تا به یک کامپوننت کاملاً عملیاتی تبدیل شود. برای استفاده از پکیج‌های اکسترنال مانند react-simple-modal (+) باید آن‌ها را به فایل package.json هم به صورت peerDependencies و هم به صورت devDependencies اضافه کنیم. وابستگی‌های زیر را به فایل اصلی package.json اضافه کنید:

"react-fade-in": "^0.1.6",
"simple-react-modal": "^0.5.1"

ساخت کتابخانه React

ساخت کتابخانه React

اکنون کافی است دستور yarn install را از دایرکتوری اصلی اجرا کنیم و همچنین دستور npm start را یک بار از دایرکتوری اصلی و بار دیگر از دایرکتوری example اجرا کنیم. اکنون می‌توانیم از پکیج‌های react-fade-in و simple-react-modal در کامپوننت خود استفاده کنیم و زمانی که توسعه‌دهندگان پکیج ما را نصب کردند، peerDependencies مرتبط با آن را نیز نصب کنند. اینک می‌توانیم در عمل کامپوننت خود را بسازیم.

ما یک API ساختیم که فید RSS کاربر مدیوم را واکشی می‌کند و روی Heroku میزبانی شده است. با توجه به حیطه موضوعی این مقاله از شما می‌خواهیم که صرفاً این کامپوننت را در مسیر src/index.js کپی کنید، زیرا می‌خواهیم بر روی انتشار کامپوننت روی NPM متمرکز باشیم. کد زیر را در فایل src/index.js وارد کنید و همه محتوای قبلی را جایگزین کنید:

1import React, { Component } from "react";
2import PropTypes from "prop-types";
3import MediumLogo from "./medium.png";
4import Modal, { closeStyle } from "simple-react-modal";
5import FadeIn from "react-fade-in";
6
7export default class MediumProfile extends Component {
8  constructor(props) {
9    super(props);
10    this.state = {
11      loading: true,
12      title: undefined,
13      image: undefined,
14      items: undefined,
15      error: false,
16      show: false
17    };
18    this.open = this.open.bind(this);
19  }
20  static defaultProps = {
21    username: "dee.aye.en",
22    size: 100
23  };
24  static propTypes = {
25    username: PropTypes.string,
26    size: PropTypes.number
27  };
28  open() {
29    this.setState({ show: true, loading: true });
30    fetch(
31      `https://medium-profile-fetch.herokuapp.com/profile/@${this.props.username}`
32    )
33      .then(result => result.json())
34      .then(data => {
35        if (data.status === "ok") {
36          this.setState({
37            items: data.items,
38            title: data.feed.title,
39            image: data.feed.image,
40            loading: false
41          });
42        } else {
43          this.setState({
44            items: [],
45            image: MediumLogo,
46            error: true,
47            loading: false
48          });
49        }
50      });
51  }
52  render() {
53    return (
54      <div
55        style={{
56          width: `${this.props.size}px`,
57          height: `${this.props.size}px`,
58          cursor: "pointer",
59          boxShadow:
60            "0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)"
61        }}
62      >
63        <img
64          onClick={() => this.open()}
65          style={{ width: "100%", height: "100%" }}
66          src={MediumLogo}
67        />
68        <Modal
69          style={{ backgroundColor: "rgba(0,0,0,0.6)" }}
70          containerStyle={{
71            border: "2px solid black",
72            padding: "15px",
73            width: "70vw",
74            cursor: "auto",
75            boxShadow:
76              "0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)"
77          }}
78          closeOnOuterClick={true}
79          show={this.state.show}
80          onClose={() => this.setState({ show: false })}
81        >
82          <a
83            style={{ ...closeStyle, color: "white", backgroundColor: "black" }}
84            onClick={() => this.setState({ show: false })}
85          >
86            X
87          </a>
88          {!this.state.loading ? (
89            <div
90              style={{
91                display: "flex",
92                alignItems: "center",
93                justifyContent: "center",
94                flexDirection: "column"
95              }}
96            >
97              <img
98                src={this.state.image}
99                width="80px"
100                style={{
101                  borderRadius: 40,
102                  marginTop: "5px",
103                  marginBottom: "15px",
104                  boxShadow:
105                    "0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)"
106                }}
107              />
108              <div
109                style={{
110                  borderBottom: "1px solid lightgray",
111                  marginBottom: "10px",
112                  paddingBottom: "20px",
113                  display: "flex",
114                  alignItems: "center"
115                }}
116              >
117                {!this.state.error ? (
118                  <h5
119                    style={{
120                      margin: 0,
121                      padding: 0,
122                      fontWeight: "lighter"
123                    }}
124                  >
125                    Latest activity by{" "}
126                    <a
127                      style={{ fontWeight: "bolder", color: "black" }}
128                      href={`https://medium.com/@${this.props.username}`}
129                      target="_blank"
130                      rel="noopener noreferrer"
131                    >
132                      {this.state.items.length > 0
133                        ? this.state.items[0].author
134                        : this.props.username}
135                    </a>{" "}
136                    on
137                  </h5>
138                ) : (
139                  <h5
140                    style={{
141                      margin: 0,
142                      padding: 0,
143                      fontWeight: "lighter"
144                    }}
145                  >
146                    User not found
147                  </h5>
148                )}
149                <img
150                  style={{ marginLeft: "5px" }}
151                  src={MediumLogo}
152                  width="20px"
153                />
154              </div>
155              <FadeIn>
156                {this.state.items.map((item, index) => (
157                  <div
158                    key={index}
159                    style={{
160                      display: "flex",
161                      alignItems: "center",
162                      padding: "10px"
163                      //width: "90%"
164                    }}
165                  >
166                    <img
167                      style={{
168                        //border: "1px solid black",
169                        marginRight: "20px",
170                        boxShadow:
171                          "0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)"
172                      }}
173                      src={
174                        item.content[2] !== "p" ? item.thumbnail : MediumLogo
175                      }
176                      width="80px"
177                    />
178                    <div
179                      style={{
180                        margin: 0,
181                        padding: 0,
182                        color: "gray",
183                        fontWeight: "lighter"
184                      }}
185                    >
186                      <a
187                        style={{ color: "gray" }}
188                        target="_blank"
189                        rel="noopener noreferrer"
190                        href={item.link}
191                      >
192                        {item.title}
193                      </a>
194                    </div>
195                  </div>
196                ))}
197              </FadeIn>
198            </div>
199          ) : (
200            <div>
201              <FadeIn>
202                <h3
203                  style={{
204                    margin: 0,
205                    padding: 0,
206                    textAlign: "center",
207                    color: "lightgray",
208                    margin: "100px"
209                  }}
210                >
211                  Fetching {this.props.username.toUpperCase()}...
212                </h3>
213              </FadeIn>
214            </div>
215          )}
216        </Modal>
217      </div>
218    );
219  }
220}

دکمه یک تصویر (medium.png) است و از این رو باید این تصویر را از این آدرس (+) دانلود کنید و در پوشه src/ قرار دهید. اکنون به آدرس example/src/App.js بروید و محتوای فایل را با کد زیر عوض کنید:

1import React, { Component } from "react";
2import MediumProfile from "react-medium-profile";
3
4export default class App extends Component {
5  constructor(props) {
6    super(props);
7    this.state = {
8      username: "dee.aye.en",
9      search: ""
10    };
11  }
12  render() {
13    return (
14      <div
15        style={{
16          display: "flex",
17          flexDirection: "column",
18          justifyContent: "center",
19          alignItems: "center",
20          padding: "70px"
21        }}
22      >
23        <h2 style={{}}>{"<MediumProfile />"}</h2>
24        <h5 style={{ color: "gray", marginBottom: "50px", marginTop: 0 }}>
25          npm i react-medium-profile
26        </h5>
27
28        <MediumProfile
29          username={
30            this.state.search.length > 0
31              ? this.state.search
32              : this.state.username
33          }
34        />
35        <h1 style={{ margin: 0, paddingTop: "30px" }}>
36          <span role="img" aria-label="pointing up">
37            ?
38          </span>
39        </h1>
40        <h5
41          style={{
42            textAlign: "center",
43            margin: 0,
44            paddingTop: "5px"
45          }}
46          onClick={() => this.setState({ username: "treyhuffine" })}
47        >
48          Click to fetch Medium profile
49        </h5>
50        <h6 style={{ margin: 0, paddingTop: "10px", color: "gray" }}>or</h6>
51        <input
52          style={{
53            marginTop: "10px",
54            border: "none",
55            marginBottom: this.state.search.length > 0 ? 0 : "40px",
56            padding: "3px",
57            textAlign: "center",
58            borderBottom: "1px solid black"
59          }}
60          type="text"
61          value={this.state.search}
62          onChange={e => this.setState({ search: e.target.value })}
63          placeholder="Find a profile"
64        ></input>
65        {this.state.search.length > 0 && (
66          <h6
67            style={{
68              marginTop: 0,
69              marginBottom: "40px",
70              paddingTop: "5px",
71              color: "gray"
72            }}
73          >
74            Now click the Medium button
75          </h6>
76        )}
77        <div
78          style={{
79            borderTop: "0.2px solid lightgray",
80            borderBottom: "0.2px solid lightgray",
81            width: "100%"
82          }}
83        >
84          <h4>props</h4>
85          <div style={{ marginLeft: "15px" }}>
86            <h4>
87              username: string <span style={{ color: "gray" }}>required</span>
88            </h4>
89            <h4>
90              size: number <span style={{ color: "gray" }}>optional</span>
91            </h4>
92          </div>
93        </div>
94      </div>
95    );
96  }
97}

ما صرفاً کامپوننت MediumProfile را از react-medium-profile ایمپورت کرده‌ایم و آن را به صورت یک Prop به نام username ارائه می‌کنیم که می‌تواند از سوی پروفایل واکشی شود.

انتشار روی NPM

اینک به ساده‌ترین بخش کار می‌رسیم. اسکریپت create-react-library به صورت خودکار یک دموی GitHub pages از پوشه examples ایجاد می‌کند و با استفاده از README به عنوان توضیح روی NPM منتشر می‌کند. مطمئن شوید که ابتدا با استفاده از دستور npm login وارد حساب NPM شده‌اید و سپس دستورهای زیر را اجرا کنید:

npm publish

این دستور به صورت خودکار پکیج شما را روی NPM انتشار می‌دهد.

npm run deploy

این دستور به صورت خودکار مثال را روی GitHub pages توزیع می‌کند. برای مشاهده کد کامل این پروژه به این ریپوی گیت‌هاب (+) مراجعه کنید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۰ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
levelup.gitconnected
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *