<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Woraphol Wananiyakul</title>
    <description>The latest articles on DEV Community by Woraphol Wananiyakul (@jungai).</description>
    <link>https://dev.to/jungai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F329278%2Fd005609e-bd99-4dfb-bde8-978c954cb577.jpeg</url>
      <title>DEV Community: Woraphol Wananiyakul</title>
      <link>https://dev.to/jungai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jungai"/>
    <language>en</language>
    <item>
      <title>รู้จักตัว Playwright คร่าวๆกัน</title>
      <dc:creator>Woraphol Wananiyakul</dc:creator>
      <pubDate>Mon, 22 Nov 2021 19:05:37 +0000</pubDate>
      <link>https://dev.to/jungai/ruucchaktaw-playwright-khraawkan-25m0</link>
      <guid>https://dev.to/jungai/ruucchaktaw-playwright-khraawkan-25m0</guid>
      <description>&lt;h2&gt;
  
  
  Playwright ??
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt; เป็น end-to-end testing framework ตัวนึงนอกจากโฟกัสที่การ test แล้วยังสามารถทำพวก browser automation ได้ไม่ยากเลยครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  ภาษาที่รองรับ
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;JS (TS)&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;.NET&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  สาเหตุที่ชอบ
&lt;/h2&gt;

&lt;p&gt;จากการอ่าน doc และได้ลองเล่นคร่าวๆ รู้สึกว่าเรียนรู้และใช้ง่ายไม่ยากเท่าไร&lt;/p&gt;

&lt;p&gt;เนื่องจากยังไม่ได้ลองเล่นทุก feature ของมันแต่เท่าที่อ่านคร่าวๆแล้วรู้สึกชอบก็คือ &lt;a href="https://playwright.dev/docs/codegen#generate-tests"&gt;codegen&lt;/a&gt; รัน script ขึ้นมาแล้วแล้วมันจะ generate code ให้เราตามที่เรา action กับตัว browser 🤩&lt;/p&gt;

&lt;p&gt;ทีนี้ผมได้ลองเอา playwright มาใช้ใน 3 usecase คือ&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;web scraping -&amp;gt; &lt;code&gt;go&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;generate pdf (as a server) -&amp;gt; &lt;code&gt;ts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;testing e2e (frontend) -&amp;gt; &lt;code&gt;ts&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ในบทความนี้จะขอพูดถึงแค่ตัวอย่างแรกส่วนสองกับสามจะไปในบทความถัดไป&lt;/p&gt;

&lt;h1&gt;
  
  
  ตัวอย่าง web scraping
&lt;/h1&gt;

&lt;p&gt;โจทย์ก็แสนง่ายให้เข้าไปที่เว็บ &lt;code&gt;mangaplus&lt;/code&gt; ค้นหา anime ที่ชื่อ &lt;code&gt;onepiece&lt;/code&gt;&lt;br&gt;
แล้วก็ไปหาว่าตอนล่าสุดตอนนี้คือ &lt;strong&gt;ตอนที่เท่าไรและชื่อตอนอะไร&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เนื่องจากโจทย์ไม่ค่อยยากผมก็เลยเลือกภาษาที่ผมได้ลองเขียนได้ไม่นานนั่นก็คือภาษา &lt;code&gt;go&lt;/code&gt; (ส่วนโจทย์ 2,3 จะเป็น javascript)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/mxschmitt/playwright-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not start playwright: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;pw&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chromium&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BrowserTypeLaunchOptions&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;SlowMo&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;800.00&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Headless&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;playwright&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not launch chromium: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not create page: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://mangaplus.shueisha.co.jp/updates"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not goto: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`[placeholder="ค้นหาตามชื่อเรื่องหรือผู้แต่ง"]`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"one piece"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not fill: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Keyboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Enter"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not press: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text=วันพีซ"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not click: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuerySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text=ตอนที่"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not query selector all: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;latestChapter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextContent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"could not get text content: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-----------------------------"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ตอนล่าสุด -&amp;gt; %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;latestChapter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-----------------------------"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;อธิบายโค้ดข้างบน&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;ให้ทำการเปิด browser ขึ้นมาโดยจะใช้เป็น chromium โดยตั้งค่าให้มี slow-motion หน่อยเพื่อให้เห็นการทำงานมันไม่เร็วเกินไปและตั้งค่าให้ headless เป็น false เพื่อที่จะดู ui&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;สร้างหน้าขึ้นมาแล้วไปเว็บ mangaplus&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;search &lt;code&gt;onepiece&lt;/code&gt; ลงในช่องค้นหา&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ค้นหาตอนล่าสุด&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://app.warp.dev/block/JspUspoZdmIGIGMqYqz683"&gt;ผลลัพธ์&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jungai/playwright-with-go"&gt;source code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ทั้งหมดนี้ก็แค่เป็นตัวอย่างเล็กๆตัวอย่างเดียวเท่านั้นครับ บทความถัดไปน่าจะเป็นการใช้ playwright มาเป็นตัวสร้าง pdf ให้เรากันครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  Refs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://playwright.dev/"&gt;https://playwright.dev/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ถ้าเกิดผิดพลาดไปตรงไหนขออภัยด้วยนะครับ 🙏&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>go</category>
      <category>thai</category>
    </item>
    <item>
      <title>ทำไมเรายังไม่ Warp (terminal)</title>
      <dc:creator>Woraphol Wananiyakul</dc:creator>
      <pubDate>Wed, 17 Nov 2021 12:27:43 +0000</pubDate>
      <link>https://dev.to/jungai/thamaimeraayangaim-warp-terminal-3mo5</link>
      <guid>https://dev.to/jungai/thamaimeraayangaim-warp-terminal-3mo5</guid>
      <description>&lt;p&gt;สวัสดีครับวันนี้จะมาพูดถึง terminal ตัวใหม่ที่ผม request access ไปประมาณ 1 เดือนและได้ใช้งานมาสักพักก็เลยจะมาแชร์ประสบการณ์การใช้งานเจ้า warp กันน&lt;/p&gt;

&lt;h2&gt;
  
  
  Warp
&lt;/h2&gt;

&lt;p&gt;คำนิยามจากตัว website ของเขา&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.warp.dev/"&gt;Warp&lt;/a&gt; is a blazingly fast, Rust-based terminal that makes you and your team more productive at coding and DevOps&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ความรู้สึก
&lt;/h2&gt;

&lt;p&gt;ผมย้ายจาก iterm2 มา warp ก็ค่อนข้างประทับใจในเรื่องของหน้าตาที่ไม่คุ้นชินและก็จะมีลูกเล่นใหม่ๆเข้ามาครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j1jKmVP8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1xqe8vgfrxzhu1rmol5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j1jKmVP8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d1xqe8vgfrxzhu1rmol5.png" alt="Image description" width="880" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;อาจจะพูดไม่หมดทุก feature เพราะคิดว่าตัวอื่นๆก็มี feature เหล่านี้อยู่แล้ว&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Block&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;คือกลุ่มของ command, output ที่เราเรียกใช้ไปก่อนหน้านี้ครับโดยทุกคำสั่งและผลลัพธ์จะถูกแบ่งออกเป็นกลุ่มๆ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zO3JPQjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3jn2fjf78exppdwym179.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zO3JPQjZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3jn2fjf78exppdwym179.png" alt="Image description" width="880" height="714"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;จากรูปข้างบนสังเกตก้อนที่เป็นสีฟ้านะครับจะเป็นกลุ่มของคำสั่ง &lt;code&gt;ls&lt;/code&gt; ที่ผมเรียกใช้และจะมี output เมื่อผมใช้นะครับ&lt;/p&gt;

&lt;p&gt;ส่วนก้อนด้านล่างคืออีกกลุ่มที่ใช้คำสั่ง &lt;code&gt;date&lt;/code&gt; ครับ&lt;/p&gt;

&lt;p&gt;แต่กลุ่มมี options ให้เราเรียกใช้อย่างเช่น &lt;code&gt;copy command&lt;/code&gt;, &lt;code&gt;copy output&lt;/code&gt; และมี option ที่ผมชอบคือแชร์ครับ เราสามารถแชร์ block ไปให้คนอื่นได้โดย warp จะสร้าง url มาให้เรานะครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.warp.dev/block/QcITCEMsoqzXb96VUqdj6M"&gt;example&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Input Editor (โครตชอบ)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ในช่อง text area จะไม่เหมือนพวก terminal ทั่วไปครับแต่มันจะเหมือน text editor เลย, การ bind ปุ่มต่างๆทำให้สะดวกต่อการขยับ cursor ไปมา, สามารถทำ multiline command ได้ด้วย&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y6k8DanX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1fbxxmbn4v35bcxddcg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y6k8DanX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s1fbxxmbn4v35bcxddcg.png" alt="Image description" width="880" height="706"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Tab Completions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;feature ตัวนี้ถ้าใครไม่เคยใช้ &lt;a href="https://fig.io/"&gt;fig&lt;/a&gt; จะว้าวมากๆคือมันสามารถ auto completions ให้เราได้เหมือนพวก IDE(รวมถึงพวก scripts ใน package.json, makefile เป็นต้น) โดยการกด `tab&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UNGPzEL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzgrbubsmp6n7oeov4d0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UNGPzEL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzgrbubsmp6n7oeov4d0.png" alt="Image description" width="880" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QpAFqSwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f550pewzj8rpoz0ys8lg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QpAFqSwQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f550pewzj8rpoz0ys8lg.png" alt="Image description" width="880" height="717"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Themes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;มี build-in มาให้ซึ่งแต่ละตัวก็น่าจะรู้จักกันอยู่แล้วและเรายังสามารถ &lt;a href="https://github.com/warpdotdev/themes"&gt;custom&lt;/a&gt; ได้เองอีกด้วย&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XNDafUmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xso1r99v3ldzmo7081n9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XNDafUmO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xso1r99v3ldzmo7081n9.png" alt="Image description" width="880" height="714"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Command Palette&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;เราสามารถเรียกใช้งานตัว &lt;code&gt;command palette&lt;/code&gt; ได้ตลอดเวลา&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--edCF4irW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kfhavr6tqnt3stxcxiom.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--edCF4irW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kfhavr6tqnt3stxcxiom.png" alt="Image description" width="880" height="699"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;Panes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หลังจากที่ผมได้เขียน post เกี่ยวกับ tmux ไปพอได้ย้ายมาตัว warp ก็ขอลอง feature นี้ซะหน่อยและรู้สึกชอบ ui มากจนคิดว่าหรือว่าจะใช้อันนี้แทน tmux ดีหรือเปล่าถถถถ หน้าตามันก็จะประมาณนี้&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uhe0QzWW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b68jkhrsh0b6r6gsb7qd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uhe0QzWW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b68jkhrsh0b6r6gsb7qd.png" alt="Image description" width="880" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;เป็น terminal ที่รู้สึกว่าดีเลยทีเดียวใช้งานง่ายไม่ยากและช่วยอะไรหลายๆอย่าง&lt;/p&gt;

&lt;p&gt;การจะใช้งานตอนนี้ต้องขอ access ผ่านตัวเว็บครับแล้วก็รองรับแค่ macos แต่ในอนาคตไปทุกที่ (linux,window,browser)&lt;/p&gt;

&lt;p&gt;สามารถเข้าไปขอ access ได้ที่หน้าเว็บเลยครับหรือ&lt;a href="https://www.warp.dev/"&gt;จิ้มตรงนี้&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;สุดท้ายนี้ happy coding ครับ 🙏&lt;/p&gt;

</description>
      <category>thai</category>
      <category>terminal</category>
      <category>warp</category>
    </item>
    <item>
      <title>แชร์ประสบการณ์ลอง migrate จาก pnpm ไป yarn berry กัน</title>
      <dc:creator>Woraphol Wananiyakul</dc:creator>
      <pubDate>Sat, 13 Nov 2021 11:33:50 +0000</pubDate>
      <link>https://dev.to/jungai/aechrprasbkaarnlng-migrate-cchaak-pnpm-aip-yarn-berry-kan-2kac</link>
      <guid>https://dev.to/jungai/aechrprasbkaarnlng-migrate-cchaak-pnpm-aip-yarn-berry-kan-2kac</guid>
      <description>&lt;p&gt;เนื่องจากได้เห็นโพสต์ที่เกี่ยวกับ yarn berry แล้วเลยอยากลองที่จะ migrate จากตัวที่ใช้ pnpm ไป berry ก็เลยเกิดเป็นโพสต์นี้ครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  ก่อนเริ่ม
&lt;/h2&gt;

&lt;p&gt;จริงๆแล้วผมเคยได้ยินyarn (berry) มานานมาแล้วแต่ไม่เคยลองเพราะว่าช่วงนั้นผมเคยเห็น issue ที่ใช้แล้ว project ไม่สามารถรันได้เลยก็เลยคิดว่ารอสักพักค่อยมาลองสุดท้ายก็ลืม -.- &lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;p&gt;ตั้งแต่เข้าสู่วงการ dev ผมก็เริ่มที่ npm -&amp;gt; yarn(classic) -&amp;gt; pnpm(ปัจจุบัน)&lt;/p&gt;

&lt;p&gt;ขอย้อนไปตอนที่ใช้ yarn classic (หลังจากนี้จะขอย่อว่า classic) ผมใช้ feature นึงคือตัว &lt;a href="https://classic.yarnpkg.com/en/docs/workspaces"&gt;worksapces&lt;/a&gt;ใช้มาประมาณ 3 ปีได้แล้วถ้านับไม่ผิด และก็เมื่อไม่นานมานี้ผมได้เห็นตัว pnpm เป็นที่พูดถึงกันก็เลยลองไปดูว่ามันเป็นยังไงมี feature ไหนบ้าง&lt;/p&gt;

&lt;p&gt;หลังจากที่ไปดูว่าโดยรวมถือว่าโอเคเลยมีทุก feature ที่ผมใช้หลังจากนั้นโปรเจคช่วงๆหลังผมจะเป็น pnpm ทั้งหมด&lt;/p&gt;

&lt;h2&gt;
  
  
  ปัจจุบัน
&lt;/h2&gt;

&lt;p&gt;ก่อนที่จะเข้าหัวข้อหลักคือ yarn berry มาดูตัวอย่าง structure ที่ผมใช้ pnpm แบบอย่างง่ายตาม example เลยก็ว่าได้แล้วจะแปลงเป็นในรูปแบบของ yarn berry&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── packages 
│   └── moduleA
│         └── package.json ──
│   └── moduleB              │ 
│         └── package.json   │  moduleC เรียกใช้ moduleA
│   └── moduleC              │ 
│         └── package.json __│ 
├── package.json 
├── pnpm-workspace.yaml // &amp;lt;- pnpm เอาไว้ใช้ประกาศ workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;packages รวม modules ต่างๆไว้ในนี้, submodule จะเป็น typescript project ทั้งหมด&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ตัวอย่างโค้ดของแบบ pnpm อยู่ repo นี้ -&amp;gt; &lt;a href="https://github.com/jungai/migrate-pnpm-to-berry/tree/pnpm"&gt;example pnpm code&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;required nodejs &amp;gt;= 14.7&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;อธิบายเพิ่มเติมจาก code &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;module{A,B,C} จะเป็น typescript ทั้งหมดโดยจะให้มัน compile ออกมาเป็น esm format รวมถึง *.d.ts เผื่อให้แต่ละ module มี type ของตัวมันเอง&lt;/li&gt;
&lt;li&gt;moduleC จะเรียกใช้ moduleA สังเกตได้จากตัว &lt;a href="https://github.com/jungai/migrate-pnpm-to-berry/blob/pnpm/packages/moduleC/package.json"&gt;package.json&lt;/a&gt; และจะเรียกใช้ใน
&lt;a href="https://github.com/jungai/migrate-pnpm-to-berry/blob/pnpm/packages/moduleC/src/index.ts"&gt;index.ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ทั้งหมดนี้เมื่อ compile และ run จะทำงานได้ถูกต้อง&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;getTheBestSong&lt;/code&gt; จาก moduleA จะเกิด Bug ไม่ว่าเราจะใส่ parameter ให้หรือไม่ใส่มันก็จะออกแต่ &lt;a href="https://www.youtube.com/watch?v=sqgxcCjD04s"&gt;strawberry moon&lt;/a&gt; ❤️&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Yarn (Berry)
&lt;/h2&gt;

&lt;p&gt;หลังจากที่ไม่ได้พูดถึงเลยก็จะขอกลับมาพูดแบบย่อสักนิด&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อดีคร่าวๆของตัว berry&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;เปลี่ยนการ resolve dependency ใหม่&lt;/li&gt;
&lt;li&gt;เพิ่ม ecosystem ของตัวเอง (plugin) e.g. &lt;code&gt;plugin-typescript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;constraints e.g. ในแต่ละ workspaces ต้องมี dependencies, library, devDependencies ที่เวอร์ชั่นเท่ากัน&lt;/li&gt;
&lt;li&gt;offline Cache&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;และแน่นอน workspaces&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อเสีย&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ส่วนตัวว่า doc ยังทำความเข้าใจได้อยากอยู่และตัวอย่างน้อย&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;อ่านเพิ่มเติมได้ที่ &lt;a href="https://yarnpkg.com/features/pnp"&gt;doc&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ปรับ pnpm ให้อยู่ในรูปแบบ berry
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;tools&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set yarn version berry ด้วย command &lt;code&gt;yarn set version berry&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;ลง &lt;a href="https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs"&gt;vscode extension&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;run command &lt;code&gt;yarn dlx @yarnpkg/sdks vscode&lt;/code&gt; (เพื่อให้ vscode รู้การ resolve ที่ไม่ใช่จากตัว node_module)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;จากโค้ด pnpm ข้างบน&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ลบ folder node_modules/ (แน่นอนจะไม่มี node_module แล้ว)&lt;/li&gt;
&lt;li&gt;ลบ pnpm-workspace.yaml, pnpm-lock.yaml&lt;/li&gt;
&lt;li&gt;เพิ่ม workspaces ใน &lt;a href="https://github.com/jungai/migrate-pnpm-to-berry/blob/master/package.json"&gt;package.json&lt;/a&gt; ใน root project&lt;/li&gt;
&lt;li&gt;ใน moduleC เรียกใช้ workspace moduleA ใน &lt;a href="https://github.com/jungai/migrate-pnpm-to-berry/blob/master/packages/moduleC/package.json"&gt;package.json&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;หลังจากทำตามข้างบนเรียบร้อยแล้วไปที่ root directory run command &lt;code&gt;yarn&lt;/code&gt; เพื่อ install dependency&lt;/p&gt;

&lt;p&gt;หลังจากที่ install แล้วจะเห็นว่ามันจะไม่มี node_module อีกต่อไปแล้วแต่จะมาในรูปแบบของ javascript file แทน ในที่นี้คือ .pnp.cjs เปรียบเสมือน node_module นั่นเองแล้วที่นี้มันจะไปดู dependencies ที่ไหนละคำตอบก็คือในโฟลเดอร์ .yarn/cache สังเกตว่า berry จะโหลด deps มาในรูปแบบ zip&lt;/p&gt;

&lt;p&gt;เรียบแล้วแล้วตอนนี้ project ของเราก็จะอยู่ในรูปแบบของ workspace(berry)&lt;br&gt;
ทีนี้ก็ลอง build ทั้งโปรเจคโดย run command &lt;code&gt;yarn workspaces foreach run build&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;มันจะแจ้งเตือนให้ลง plugin เพิ่ม (ecosystem อันใหม่) run command &lt;code&gt;yarn plugin import workspace-tools&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;เมื่อลงเสร็จลอง build ใหม่ตาม command ข้างบนทีนี้ก็เป็นการเสร็จสิ้น&lt;/p&gt;

&lt;p&gt;แต่เดี๋ยวก่อนนน...&lt;/p&gt;

&lt;p&gt;ถ้าเกิดลองไปดูที่ moduleC จะเห็นว่ามันจะแจ้งเตือนหา moduleA ไม่เจอ ซึ่งผมก็ติดอยู่ตรงนี้แหละไม่ได้ไปต่อ ซึ่งลองไปดู issue ก็จะมีคนติดปัญหาที่คล้ายๆแบบนี้ก็เลยไม่ได้ไปลอง feature อื่นๆต่อ&lt;/p&gt;

&lt;p&gt;แต่ถ้า project เป็น javascript ไม่ใช่ typescript สามารถทำงานได้ปกติ&lt;/p&gt;

&lt;p&gt;โค้ดของ berry -&amp;gt; &lt;a href="https://github.com/jungai/migrate-pnpm-to-berry"&gt;example berry code&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;ปัญหาข้างบนเป็นในกรณีที่เราใช้ตัว workspaces แต่ถ้าไม่ได้ใช้ workspaces ตัว berry ทำงานได้ปกติ (resolve ได้ปกติ)&lt;/p&gt;

&lt;p&gt;ทั้งหมดนี้ก็เป็นประสบการณ์ที่ผมได้เจอในการลองที่จะย้ายค่ายกลับมาใช้ yarn, ปัญหาที่เกิดขึ้นอาจจะเกิดที่ผมเซ็ตไม่ถูกต้องก็ได้ครับและถ้ามีโอกาสได้กลับมาแก้หรือมันแก้ไข้อะไรยังไงเรียบร้อยแล้วจะมาแชร์อีกทีครับ&lt;/p&gt;

&lt;p&gt;ทั้งหมดนี้ถ้าผมสื่อสารผิดพลาดหรือทำให้งงยังไงก็ขอโทษด้วยนะครับจะพยายามปรับปรุงเรื่อยๆครับ ขอบคุณครับ 🙏&lt;/p&gt;

&lt;h2&gt;
  
  
  Refs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://yarnpkg.com/features/pnp"&gt;https://yarnpkg.com/features/pnp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yarnpkg.com/getting-started/editor-sdks"&gt;https://yarnpkg.com/getting-started/editor-sdks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yarnpkg/berry/issues/3399"&gt;issue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>thai</category>
      <category>pnpm</category>
      <category>yarn</category>
      <category>typescript</category>
    </item>
    <item>
      <title>ทำไมผมถึงชอบ tmux</title>
      <dc:creator>Woraphol Wananiyakul</dc:creator>
      <pubDate>Sun, 07 Nov 2021 14:04:27 +0000</pubDate>
      <link>https://dev.to/jungai/thamaimphmthuengchb-tmux-bjl</link>
      <guid>https://dev.to/jungai/thamaimphmthuengchb-tmux-bjl</guid>
      <description>&lt;p&gt;สวัสดีครับวันนี้จะมาเล่าถึงเครื่องมือที่ผมใช้มาประมาณเกือบ 3 ปีได้ซึ่งตัวนั้นก็คือ tmux&lt;/p&gt;

&lt;h2&gt;
  
  
  tmux คืออะไร
&lt;/h2&gt;

&lt;p&gt;จริงๆแล้วมันก็คือ terminal multiplexer หรือก็คือเครื่องมือที่จะจัดการหน้าต่างๆใน terminal ของเราตามรูปด้านล่าง&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ySTlPdc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeqc8d640lfkjz6jus3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ySTlPdc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xeqc8d640lfkjz6jus3f.png" alt="Image description" width="880" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;จากรูปข้างบนก็จะเห็นว่าเราสามารถแบ่งหน้าต่างๆออกได้มากมายตามที่เราต้องการเช่นเรารัน frontend และ backend ใน backend มีหลาย service&lt;/p&gt;

&lt;p&gt;จริงๆแล้วมันมี tool ที่คล้ายๆแบบนี้เยอะ 1 ในนั้นก็คือที่ผมใช้มาก่อนหน้าก็คือ split pane เป็น feature หนึ่งในตัว &lt;a href="https://iterm2.com/features.html"&gt;iterm2&lt;/a&gt; ครับ&lt;/p&gt;

&lt;p&gt;เมื่อประมาณ 3 ปีที่แล้วตอนผมเริ่ม dev ใหม่ๆตอนนั้นผมใช้ macbook ส่วนตัวซึ่ง mac ตัวนั้นผมก็ลง iterm2 เอาไว้ ไอ้เจ้า iterm2 เนี่ยละครับมันมี split panes ถ้าใครอยากรู้ลองเข้าไปดูใน &lt;a href="https://iterm2.com/features.html"&gt;doc&lt;/a&gt; ได้เลยครับ&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sxwdWuwM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz9ctgfk3s16dcrj1tso.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sxwdWuwM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hz9ctgfk3s16dcrj1tso.png" alt="Image description" width="880" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;สังเกตว่าจะคล้ายๆรูปข้างบนเลย&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ทีนี้ก้ใช้เจ้า feature นี้มาเรื่อยๆจนกระทั่งผมเรียนจบแล้วเริ่มทำงานครับทีนี้ที่ทำงานให้ notebook ที่ลง linux(popos) เอาไว้ให้เป็นครั้งแรกที่ผมใช้ linux จริงๆจังๆไม่ได้แบบทำ lab ในมหาลัยที่เป็นพวก ubuntu จนกระทั่งใช้ไปใช้มาแล้วรู้สึกหงุดหงิดมากที่ไม่สามารถทำแบบ iterm2 ได้ผมก็เลยลองหาข้อมูลดูจนไปเจอ tmux แล้วผมก็ใช้มาเรื่อยๆจนถึงปัจุบัน(ถึงจะกลับมาใช้ mac แล้วแต่ก้ไม่ได้ใช้ feature ของ iterm อีกเลย)&lt;/p&gt;

&lt;p&gt;ข้อดีของ tmux ที่ผมชอบ&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;สามารถ config ได้อิสระ&lt;/li&gt;
&lt;li&gt;สามารถใช้ได้ทุก os (mac,linux,window wsl) -&amp;gt; ผมชอบสุดดดด ❤️&lt;/li&gt;
&lt;li&gt;มี ecosystem ของตัวมันเอง (theme, plugin)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ส่วนข้อเสียผมนึกไม่ออกแหะ&lt;/p&gt;

&lt;h2&gt;
  
  
  สรุป
&lt;/h2&gt;

&lt;p&gt;ทุกคนอาจจะชอบ tool ที่แตกต่างกันออกไปหรืออาจจะชอบแบบเปิดทีละ tab ก็ได้ ขึ้นอยู่กับแต่ละคนว่าชอบแบบไหนครับ&lt;/p&gt;

&lt;h2&gt;
  
  
  Refs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.totmux"&gt;tmux repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tmux-plugins/tpm"&gt;tmux plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dracula/tmux"&gt;dracula theme&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jungai/dotfiles/blob/master/tmux/.tmux.conf"&gt;my config&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>thai</category>
    </item>
    <item>
      <title>Cancel http request when component is unmounted</title>
      <dc:creator>Woraphol Wananiyakul</dc:creator>
      <pubDate>Sun, 07 Nov 2021 08:42:57 +0000</pubDate>
      <link>https://dev.to/jungai/cancel-http-request-when-component-is-unmounted-4o8i</link>
      <guid>https://dev.to/jungai/cancel-http-request-when-component-is-unmounted-4o8i</guid>
      <description>&lt;p&gt;หลังจากที่ได้ย้ายจาก &lt;code&gt;vue&lt;/code&gt; มา &lt;code&gt;react&lt;/code&gt; ได้ประมาณ6เดือน บางปัญหาที่ไม่เคยเจอใน vue เลยมาเจอจากตัว react และบทความนี้ก็เป็น1ในปัญหาที่ผมได้เจอตอนเขียน  react จริงๆจัง&lt;/p&gt;

&lt;h2&gt;
  
  
  ที่มาของปัญหา
&lt;/h2&gt;

&lt;p&gt;เวลาเราต้องสร้าง component บางตัวแล้วเราต้องการให้มันยิง http request เพื่อไปเอาข้อมูลบางอย่างมาเราจะทำตอนที่ component นั้น mount ขึ้นมาแล้วยิงไปจังหวะนั้น ดูรวมๆแล้วก็ปกติไม่มีอะไรแต่ถ้า component นั้นถูก unmount ไปในตอนที่มันยังยิง http request ไม่เสร็จมันจะเกิด error ขึ้น&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  สาเหตุ
&lt;/h2&gt;

&lt;p&gt;ปัญหาข้างบนมาจากที่ว่าเวลาเราทำการ unmouted component ไปแล้วแต่ http request มันยังคงทำงานต่อเพราะมันไม่ได้หยุด request ตัวนั้นแล้วเมื่อมันทำงานเสร็จก็จะไปเซ็ต state อันเก่า&lt;/p&gt;

&lt;p&gt;มาลองดูตัวอย่างคร่าวๆกันดีกว่า&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/some/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="nx"&gt;setSomeState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)()&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จาก code ข้างบนก็ดูปกติไม่มีอะไรแต่เราเราลองเพิ่ม delay ให้มันสักหน่อยก่อนที่จะให้่ยิง http request&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/some/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="nx"&gt;setSomeState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
     &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)()&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ก็ยังดูปกติเมื่อ component ถูก mount ขึ้นมาจะรอ 2 วินาทีหลังจากนั้นก็ยิงเพื่อไปเอาข้อมูล แต่ช้าก่อนถ้าเกิดระหว่างนี้ component ถูก unmount ล่ะ ??&lt;/p&gt;

&lt;p&gt;flow ข้างบนก็ยังคงดำเนินต่อไปจนจบแม้ว่า component ถูก unmounted ไปแล้ว ทีนี้ react มันไม่ให้เรา update state ตอนเมื่อ component ถูก unmounted ไปแล้วนั่นเอง&lt;/p&gt;

&lt;h2&gt;
  
  
  วิธีแก้
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;เช็คว่า component unmounted ไปยังถ้า unmounted ไปแล้วไม่ให้เซ็ต state ลงไป
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isUnmounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/some/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isUnmounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

       &lt;span class="nx"&gt;setSomeState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)()&lt;/span&gt;

 &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;isUnmounted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;code ข้างบนก็เป็นวิธีแก้อย่างหนึ่ง แต่การแก้ข้างบนจะแก้แค่ปัญหาที่ว่า react ไม่ให้อัพเดต state ตอน unmounted ส่วนปัญหาที่ http request ยังคงยิงไปไม่หยุดยังคงอยู่&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;แก้แบบยั่งยืน&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ใช้ตัว &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController"&gt;AbortController&lt;/a&gt; ในการ cancel http request ครับ&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ข้อดี&lt;/strong&gt;ของมันคือ มันสามารถที่จะยกเลิก request และก้ browser support (อมก)&lt;/p&gt;

&lt;p&gt;ทีนี้ก็เอาตัว abortController มา implement&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/some/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
       &lt;span class="p"&gt;})&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="nx"&gt;setSomeState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)()&lt;/span&gt;

 &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;จาก code ข้างบน เราจะใช้ &lt;code&gt;new AbortController()&lt;/code&gt; ที่นี้เมื่อ unmounted เราจะให้ &lt;code&gt;abort()&lt;/code&gt; แล้วตัวตอน fetch ก็จะใส่ options &lt;code&gt;signal&lt;/code&gt; ไป&lt;/p&gt;

&lt;p&gt;signal ตัวนี้จะเปลี่ยนเมื่อเรา call abort() ทำให้ตอนที่เรายิง htttp request ไปแล้ว signal เป็น false มันจะ cancel http request นั้นทันที (เช็คได้ใน devtools ขึ้้น error หรือดูในแถบ network จะขึ้น cancel) สิ่งที่ต้องทำเพิ่มก็คือไปเขียน trycatch เพื่อดักerror ว่ามันเป็น abort&lt;/p&gt;

&lt;h2&gt;
  
  
  เพิ่มเติ่ม
&lt;/h2&gt;

&lt;p&gt;ตัว AbortController รองรับกับ http client หลายเจ้าเพียงแค่เราใส่ตัว options ให้มันแต่จะมีบางตัวที่ใช้เราแล้วเหมือนจะเป็น bug &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API"&gt;fetch&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/todos/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rawData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/axios/axios"&gt;axios&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/sindresorhus/ky"&gt;ky&lt;/a&gt;(ตัวที่เป็นปัญหาถ้าผมไม่ได้ทำอะไรผิด)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ky&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://jsonplaceholder.typicode.com/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ตัวอย่างทั้งหมดนี้อยู่ใน github -&amp;gt; &lt;a href="https://github.com/jungai/cancel-request-in-react"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ถ้าเป็น vue ล่ะ&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;เนื่องจาก vue มี lifecycle ที่คล้ายกับตัว react แต่สุดท้ายการอัพเดต state &lt;br&gt;
แตกต่างกันแต่เราสามารถใช้วิธียกเลิก http request ได้เหมือนกัน&lt;/p&gt;

&lt;p&gt;ตัวอย่างของ vue -&amp;gt; &lt;a href="https://github.com/jungai/cancel-request-in-vue"&gt;here&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;ถ้าเกิดผมผิดพลาดตรงไหนไปก็ขอภัยด้วยครับสามารถ comment ที่ผมผิดไปหรือจะเสนอแนะอะไรได้เลยครับเนื่องจากเป็น post แรกของผมใน dev.to เลยครับขอบคุณครับ &lt;/p&gt;

</description>
      <category>react</category>
      <category>vue</category>
      <category>thai</category>
    </item>
  </channel>
</rss>
