昨天晚上趕稿,可能我的語意表達有點錯誤
要知道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畫面
於是呢,我就想自己嘗試實做看看這件事情。
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;
};
這樣子我們每個訂閱者就可以根據各自的喜好(各自想對count做不同事的function),來接收count大大的最新消息了。
[觀察者模式] (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)