有感這篇文章👉函數式編程看React Hooks(二)事件綁定反作用深度剖析,做者寫得條理很清晰。react
在此再強調一遍: 爲何要用useCallback去緩存onMouseMove這個函數?由於該函數是一個useEffect的依賴項,並且它是個引用類型的值,因此每一次re-render都會是新的函數。假若該函數未發生變化,其實沒有必要每次re-render都生成新的,這也是useCallbak最主要的做用。編程
下面延伸一丟丟,仍是講hooks的依賴項。從我本身還原的部分HashRouter代碼講起:緩存
export default function HashRouter(props) {
let locationState = null;
const [location, setlocation] = useState({
pathname: window.location.hash.slice(1) || "/",
state: locationState
});
const handleHashChange = useCallback(() => {
console.log('執行 useCallback裏面的函數') // 只要hashchange就會執行
setlocation({
...location,
pathname: window.location.hash.slice(1), // 新pathname的值並無依賴上一個pathname
state: locationState
})
}, []); // 這裏並無添加依賴
useEffect(() => {
console.log('執行useEffect函數') // 只會打印一次
window.addEventListener('hashchange', handleHashChange)
return () => {
window.removeEventListener('hashchange', handleHashChange)
}
}, []); // 這裏也沒有添加依賴
const val = {
location,
history: {
push: to => {
if (typeof to === 'object') {
let {
pathname,
state
} = to;
window.location.hash = pathname;
locationState = state;
} else {
window.location.hash = to;
}
}
}
}
return <ReactRouterContext.Provider value={val}>
{
props.children
}
</ReactRouterContext.Provider>
}
複製代碼
請看代碼中的註釋部分。是的,我在上述useCallback和useEffect中都沒有添加依賴項。其實這是很差的,會形成疑惑和不解。ide
但另外一方面,它實現了緩存函數&&只綁定一次hashchange事件&&在hashchange時正常切換頁面的設計初衷:函數式編程
當我一開始對比文章開頭引用的文章和本身這段代碼時,也感到疑惑,再仔細去篩查,發現:在setlocation時,是從外部獲取新值的這個緣由促成的。因此纔可以實現依賴項爲空,且照樣能刷新的目的。函數
下面咱們引入一組對照組,就能夠發現確實如此:post
export default function HashRouter(props) {
let locationState = null;
const [location, setlocation] = useState({
pathname: window.location.hash.slice(1) || "/",
state: locationState
});
const [count, setcount] = useState({ // +++++ 新增這一段 +++++
number: 0
})
const handleHashChange = useCallback(() => {
setcount({
number: count.number+1 // +++++ 新增這一段 +++++
});
setlocation({
...location,
pathname: window.location.hash.slice(1),
state: locationState
})
}, []);
useEffect(() => {
window.addEventListener('hashchange', handleHashChange)
return () => {
window.removeEventListener('hashchange', handleHashChange)
}
}, []);
console.log('count--------', count); // +++++ 新增這一段 +++++ //會發現count只會增長到1,由於react的比較是Object.is!無論是primitive仍是引用類型的值
console.log('location', location); // +++++ 新增這一段 +++++
// 這個之因此會變化,是由於它的計算,是從window.location.hash中設置新值的,而不是依賴前一個值
const val = {
location,
history: {
push: to => {
if (typeof to === 'object') {
let {
pathname,
state
} = to;
window.location.hash = pathname;
locationState = state;
} else {
window.location.hash = to;
}
}
}
}
return <ReactRouterContext.Provider value={val}>
{
props.children
}
</ReactRouterContext.Provider>
}
複製代碼
再看看執行這段代碼的打印結果:spa
這個結果就和上面推薦的文章的結果相同了。設計