Hook 是 React 16.8 的新增特性。它可讓你在不編寫 class 的狀況下使用 state 以及其餘的 React 特性。html
let memoizedState; //聲明memoizedState
function useState(initialState) {
memoizedState = memoizedState || initialState;
function setState(newState) {
memoizedState = newState; //設置狀態時候把新狀態賦值給memoizedState
render(); //從新render
}
return [memoizedState,setState]
}
複製代碼
從簡版的實現來看,仍是很容易理解,測試下效果react
用userReducer實現計數器,可接收3個參數,reducer | initalArg(初始值) | init (定義初始值的函數)web
import React,{Fragment,useReducer} from 'react';
import ReactDOM from 'react-dom';
// reducer,跟redux中reducer同樣
const INCREMENT = "INCREMENT";
const DECREMENT = "DECREMENT";
function reducer(state,action){
switch (action.type) {
case INCREMENT:
return {number:state.number+1};
case DECREMENT:
return {number:state.number-1};
default:
return state;
}
}
//初始值
let initalArg = 0;
// 返回初始值的函數
function init(initalArg) {
return {number:initalArg};
}
function Counter() {
// state = {number:0}
const [state,dispatch] = useReducer(reducer,initalArg,init);
return (
<Fragment>
<p>{state.number}</p>
<button onClick={()=>dispatch({type:INCREMENT})}>+</button>
<button onClick={()=>dispatch({type:DECREMENT})}>-</button>
</Fragment>
)
}
function render() {
ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();
複製代碼
let memoizedState; //聲明記憶的狀態
function useReducer(reducer,initalArg,init) {
let initialState;
// init若是沒有傳值,initalArg爲默認的初始狀態。若是傳值,初始值函數處理後做爲初始狀態
if(typeof init !='undefined'){
initialState = init(initalArg);
}else {
initialState = initalArg;
}
memoizedState = memoizedState || initialState;
function dispatch(action) {
memoizedState = reducer(memoizedState,action);
render();
}
return [memoizedState,dispatch]
}
複製代碼
運行結果redux
let memoizedState;
function useReducer(reducer,initalArg,init) {
let initialState;
if(typeof init !='undefined'){
initialState = init(initalArg);
}else {
initialState = initalArg;
}
memoizedState = memoizedState || initialState;
function dispatch(action) {
memoizedState = reducer(memoizedState,action);
render();
}
return [memoizedState,dispatch]
}
function useState(initialState) {
// 主要是reducer實現,把新狀態賦值過去
return useReducer((oldState,newState)=>newState,initialState);
}
複製代碼
驗證如下數組
當一個組件調用多個useState時,此時咱們須要用數組來保存多個初始值緩存
import React,{Fragment} from 'react';
import ReactDOM from 'react-dom';
// 數組保存memoizedState
let memoizedState=[];
// 記錄索引
let index = 0;
function useState(initialState) {
memoizedState[index] = memoizedState[index] || initialState;
// 緩存當前索引,由於每次render,index索引會重置爲0
let currentIndex = index;
function setState(newState) {
memoizedState[currentIndex] = newState;
render();
}
return [memoizedState[index++],setState]
}
function Counter() {
const [name,setName] = useState('計數器');
const [number,setNumber] = useState(0);
return (
<Fragment>
<p>{name }:{number}</p>
<button onClick={()=>setName("計數器"+Date.now())}>更名稱</button>
<button onClick={()=>setNumber(number+1)}>+</button>
</Fragment>
)
}
function render() {
// 每次render,把index回覆初始值
index = 0;
ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();
複製代碼
看下效果,源碼是用鏈表實現的,此處咱們用數組,邏輯是差很少,容易理解bash
計數器變化後,實現打印一句log的示例 參考連接dom
import React,{Fragment,useState} from 'react';
import ReactDOM from 'react-dom';
function Counter() {
const [name,setName] = useState('計數器');
const [number,setNumber] = useState(0);
useEffect(()=>{
// 訂閱
console.log("訂閱狀態")
},[number,name]);
return (
<Fragment>
<p>{name }:{number}</p>
<button onClick={()=>setName("計數器"+Date.now())}>更名稱</button>
<button onClick={()=>setNumber(number+1)}>+</button>
</Fragment>
)
}
function render() {
ReactDOM.render(<Counter />,document.getElementById('root'));
}
render();
複製代碼
簡版實現函數
// 記錄最後依賴項
let lastDependencies;
function useEffect(callback,dependencies) {
// 若是依賴項沒有傳值,則直接調用callback
if(!dependencies) return callback();
/* 一、首次渲染isChange爲true,把初始的依賴項賦給lastDependencies
* 二、再次渲染時候,把lastDependencies和dependencies作對比,當不徹底相等時,才觸發回調
*/
let isChange = lastDependencies? !dependencies.every((item,index)=>item===lastDependencies[index]):true;
if(isChange){
callback();
lastDependencies = dependencies;
}
}
複製代碼
let memoizedState=[];
let index = 0;
function useState(initialState) {
memoizedState[index] = memoizedState[index] || initialState;
let currentIndex = index;
function setState(newState) {
memoizedState[currentIndex] = newState;
render();
}
return [memoizedState[index++],setState]
}
function useEffect(callback,dependencies) {
console.log('dependencies',dependencies);
// 若是依賴項沒有傳值,則直接調用callback
if(!dependencies){
// 保證索引對應
index++;
return callback();
}
// 從memoizedState取最後一個依賴項
let lastDependencies = memoizedState[index];
let isChange = lastDependencies? !dependencies.every((item,index)=>item===lastDependencies[index]):true;
if(isChange){
callback();
// 往memoizedState存依賴項
memoizedState[index] = dependencies;
}
// 索引遞增
index++
}
複製代碼
驗證下效果測試
後續繼續補充