iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0
Modern Web

關於React,那些我不知道的系列 第 2

關於 render 與 re-render,那些React不說,但默默幫我們做的事(內容比起React跟Observer Pattern 更有關)

  • 分享至 

  • xImage
  •  

昨天晚上趕稿,可能我的語意表達有點錯誤

要知道ref特別在哪,就要看React 一般的狀態控制,有比較有差別XD,就決定明天來看看state/props囉!

這裡我只是想回應從ref 的跳脫畫面渲染(下):

React ref Why? 跳脫/跨越React因為狀態(state/props)改變而導致畫面的重新渲染(rerender)、實際操作DOM元素

這個地方回來談談自動的動態畫面渲染(render)

先說我不懂React底層操縱畫面更新的機制,我只知道React底下有個virtual-DOM 以及diff演算法。透過比較Component之間的差異,來決定是否更新這個Component跟它底下的children。

[2020/09/19補充] 來看看去年別人談的React 渲染
【Day 21】React 渲染機制

之前有嘗試使用原生JavaScript做資料改變時,同步動態更新畫面,我只知道自己做這件事有點麻煩XD。

昨天的例子,有顆遞增按鈕點了後,因為state改變,使畫面上的數字(state)被React rerender畫面
https://ithelp.ithome.com.tw/upload/images/20200917/20130721a5uS4M06nQ.png

於是呢,我就想自己嘗試實做看看這件事情。

What 假設我們有個變數count,當我們每次改變count時,所有顯示在畫面上跟count有關的DOM都要被更新(rerender)

先來個超暴力解

let count = 1;
const increment = () => {
  count = count + 1;
  countDisplay.innerHTML = count;
};
btn.addEventListener("click", increment);

簡單點或許可以這樣
但假設我們有在click(點擊事件)以外的地方改變count呢?
(比如說有個setTimeout 也改變count呢?)
或我們也有在其他地方顯示count呢?

WHY? 藉由原生JS的不便,體會React帶來的便利 QwQ

How? 解決辦法百百種,網路上查一查,再加上自己之前就有點想嘗試的東西,這次就決定試看能不能自己使用Observer Pattern 完成。

這麼帥氣的方法肯定一堆大大們做過了啊, 不過小弟還是想嘗試看看、獻醜一下,方法不漂亮,但有錯還請大家多多包容,指教一下。讓小弟有機會好好學習、進步。

show me the code
codesandbox

    <!-- 顯示當前count -->
<h2>
    現在Count 是
    <span id="count-display"></span>
</h2>

    <!-- 從1開始,顯示1~count -->
<h2>
    其他也用到count的地方
    <span id="other-count-display"></span>
</h2>
const btn = document.getElementById("increment-btn");
const countDisplay = document.getElementById("count-display");
const otherCountDisplay = document.getElementById("other-count-display");

let pub1 = new Publish(initCount); // initCount 是 1

let sub1 = function (pub) { countDisplay.innerHTML = pub.count; };
sub1.subscribe(pub1);
// =====================================
let sub2 = function (pub) {
  let CustomCountString = "";
  for (let i = 1; i <= pub.count; i = i + 1) {
    CustomCountString = `${CustomCountString} ${i}`;
  }
  otherCountDisplay.innerHTML = CustomCountString;
};
sub2.subscribe(pub1);
// =====================================
btn.onclick = function () {
  pub1.increment();
  pub1.deliver();
};

codesandbox
首先呢,我們希望有個count的數字可以被觀察,當count改變時,其他有 按讚分享點開小鈴鐺 count的人,就會收到最新的通知。也就是我們這些點讚/訂閱的人就是觀察者,count大大就是被我們觀察的對象

因此我們就先來實作發布最新消息的Youtube 界 網紅count 大大本人

// count 大大的contructor
let Publish = function (count) {
  this.count = count; // 每位網紅都有自己的count
  this.subscribers = []; // 每位網紅都有自己的受眾/訂閱者們
  this.increment = function () {
    this.count = this.count + 1;
  };
};
// 發布消息
Publish.prototype.deliver = function () {
  let publish = this;
  this.subscribers.forEach(function (item) {
    // 每個訂閱者item都是一個function
    // 所以當deliver發生時,我們就去執行所有訂閱者(並帶入發布者最新消息)
    item(publish);
  });
  return this;
};

codesandbox
接著再輪到我們這些受眾們

// 小朋友不要亂修改原生物件唷QwQ ESlint會噴警告,我是壞小孩XD
Function.prototype.subscribe = function (publish) {
  // 取得訂閱者本人
  let sub = this;
  // 確認你有沒有訂閱過這個網紅了
  let alreadyExisted = publish.subscribers.some(function (item) {
    return item === sub;
  });
  // 還記得我們在Publish建構子(constructor)時,有個subscribers的Array
  // 你之前還沒開起小鈴鐺訂閱過的話,就把你收入近這個網紅清單
  if (!alreadyExisted) {
    publish.subscribers.push(sub);
  }
  return this;
};

https://ithelp.ithome.com.tw/upload/images/20200917/2013072196xbmotsRu.png

這樣子我們每個訂閱者就可以根據各自的喜好(各自想對count做不同事的function),來接收count大大的最新消息了。

codesandbox

參考資料

[觀察者模式] (https://kknews.cc/code/plgjyej.html)
[React Diff] (https://zhuanlan.zhihu.com/p/20346379)
[Design Pattern | 只要你想知道,我就告訴你 - 觀察者模式( Observer Pattern ) feat. TypeScript by神 Q 超人] (https://medium.com/enjoy-life-enjoy-coding/design-pattern-%E5%8F%AA%E8%A6%81%E4%BD%A0%E6%83%B3%E7%9F%A5%E9%81%93-%E6%88%91%E5%B0%B1%E5%91%8A%E8%A8%B4%E4%BD%A0-%E8%A7%80%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F-observer-pattern-feat-typescript-8c15dcb21622)


上一篇
那個ref,幹啥用的、怎麼用?
下一篇
來個聰明的傢伙,幫我記憶一些資料吧! 出來吧 useMemo (9/19 修正描述)
系列文
關於React,那些我不知道的30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言