簡單的 useState 實作
本文寫于 2020 年 10 月 21 日
以下是一段非常簡單的 React 代碼:
const App = () => {
const [n, setN] = useState(0);
return (
<div>
{n}
<button onClick={() => setN(x => x + 1)}>+1</button>
</div>
);
}
React.render(<App />, rootElement)
這樣的用法和以往的 setState 是有明顯的不同的,他看起來更像 redux——我們初始化一個 state,然后 dispatch 一個 action,再由 reducer 改變 state 后回傳新的 state,
Redux 思想實作 useState
既然我們覺得它像,那我們就來自己實作一個吧,
不熟悉 Redux 思想的同學請自行閱讀檔案
const useState = (initialValue) => {
let state = initialValue;
const dispatch = (newState) => {
state = newState;
render(<App />, document.getElementById('root'));
};
return [state, dispatch];
};
然后我們用這個自定義的 useState 代替 React 的 useState——就會發現我們失敗了,setN 無論如何都不會有任何反應,
這是因為我們每次重新 render 的時候都重新執行了函式,于是我們總是會重新賦值,
為什么不會重新賦值?
對于這段 React 代碼來說,當我們第一次運行時,React 會進行首次渲染,即 render(<App />, ...),
在此程序中,會先呼叫 App(),之后便會得到虛擬 DOM,再創建真實的 Div,
當我們觸發點擊事件時,會呼叫 setN,再次 render(),之后呼叫 App(),然后得到新的虛擬 DOM,進行 diff 演算法,根據 diff 演算法的結果去更新新的 Div,
而不管是第一次渲染,還是第二次呼叫,都會呼叫 useState(),
但是我們寫的是 useState(0) 啊,兩次呼叫明明是一樣的代碼,為何 n 的值不同?怎么解決這個問題呢?
很簡單,閉包嘛,
const createUseState = () => {
let state;
const useState = (initialValue) => {
if (!state) {
state = initialValue;
}
const dispatch = (newState) => {
state = newState;
render(<App />, document.getElementById('root'));
};
return [state, dispatch];
};
};
這樣就解決了重新賦值的問題,
多次呼叫
但是我們需要多次呼叫 useState 呀,不可能只用一次的,
于是我們將 state 改為一個陣列:const state = [];,
const createUseState = () => {
const state = [];
let index = 0;
return (initialValue) => {
state[index] = state[index] || initialValue;
const currentIndex = index;
const dispatch = (newState) => {
state[currentIndex] = newState;
// 重點
index = 0;
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
};
return [state[index++], dispatch];
};
};
我們創建了一個 index 變數來控制索引,它需要我們保證每次重新渲染 App 傳入陣列的元素是一樣的——這就是為什么我們不可以將 useState 寫在 if 判斷中,
在上述代碼中有一處重點,在于我們需要在每次 set 之后將索引歸零 index = 0,
因為每次 render 結束后,React 都會重新執行該函式,
(完)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/185786.html
標籤:JavaScript
