再此之前你已經(jīng)學(xué)習(xí)了:
React18(函數(shù)式開發(fā))+Ts入門開發(fā)(一)創(chuàng)建Ts項(xiàng)目使用SCSS店展、antdUI庫
React18(函數(shù)式開發(fā))+Ts入門開發(fā)(二)路由配置以及路由的基礎(chǔ)使用
今天第三講就講講復(fù)制傳值以及hooks的實(shí)際應(yīng)用吧。
PS:此頁面我已經(jīng)事先創(chuàng)建好,并且建好路由了。不會創(chuàng)建頁面建路由的看上面一二兩篇筹麸。本篇就不重復(fù)講述怎么創(chuàng)建頁面和路由了炕泳。
一 初識useEffect,先簡單介紹下它是什么
為什么React18后函數(shù)式開發(fā)火了苞七,就是因?yàn)?8之前函數(shù)式組件是沒有生命周期的,導(dǎo)致它不能滿足某些特殊需求挪丢。而有了hooks
后蹂风,他就有了自己的生命周期。
當(dāng)然乾蓬,我這里只是簡單介紹一下以及我們開發(fā)常用的惠啄。更詳細(xì)的需要看官方文檔了。
1. 模擬componentDidMount第一次渲染
useEffect(() => {
console.log('');
}, []) // 第二個(gè)參數(shù)為空時(shí)只會在第一次渲染時(shí)執(zhí)行
2. 模擬componentDidUpdate
useEffect(() => {
console.log('n變化了');
}, [n]) // 第二個(gè)參數(shù)為要監(jiān)聽的數(shù)據(jù)
// 不傳第二個(gè)參數(shù),則會在 state 的任意一個(gè)屬性改變時(shí)都會觸發(fā)該函數(shù)回調(diào)
useEffect(() => {
console.log('任意屬性變化');
})
3. 模擬componentWillUnmount
useEffect(() => {
console.log('任意屬性變了');
return () => {
console.log('該組件要銷毀了');
}
})
二 初識useState 我們先聲明兩個(gè)變量撵渡,并且賦值融柬。以及useEffect 的大概使用
1. 基礎(chǔ)賦值
import { useState, useEffect } from "react";
// 父組件
function HooksDemo() {
// 定義一個(gè) num 初始為 0
// 定義一個(gè) isBoolean 初始為 false
const [num, setNum] = useState(0);
const [isBoolean,setBoolean] = useState(false);
// 等同于Vue
// data() {
// return {
// num:0,
// isBoolean:false,
// }
// }
// 初始化執(zhí)行
useEffect(()=>{
setNum(5);
console.log(num); // 你以為是輸出5,實(shí)際是 0 也就是上一次的值
setBoolean(true)
console.log(isBoolean); // 你以為是輸出true姥闭,實(shí)際是 false 也就是上一次的值
},[]);
// 等同于Vue
// mounted(){
// this.num = 5;
// console.log(num); // 輸出5
// this.isBoolean = true;
// console.log(num); // 輸出true
// }
return <div className="home">hooksDemo 示例</div>;
}
export default HooksDemo;
2. 其他賦值
有時(shí)候我們可能定義的是一個(gè)對象丹鸿,或者其他的,只需要改變其中的某一個(gè)值棚品。并不需要全部改變靠欢。
這種情況怎么辦呢?
const [user,setUser] = useState({name:"zs",age:20});
// demo1
setUser(Object.assign(user,{age:18})); // {name: 'zs', age: 18}
// demo2
const changeUser = (num: number) => {
setUser((state) => {
let { name, age } = state;
age = num;
return {
name,
age,
};
});
};
三 通過useCallback同步獲取Set后的值
從上面我們發(fā)現(xiàn)一個(gè)問題铜跑,就是當(dāng)我們set值后门怪,無法同步獲取。其實(shí)React中的setState本身執(zhí)行的過程和代碼是同步的(比如你HTML里{num} 顯示的就是5)
锅纺,只是因?yàn)?React 框架本身的性能優(yōu)化機(jī)制而導(dǎo)致的掷空。
React 中合成事件和生命周期函數(shù)的調(diào)用順序在更新之前,導(dǎo)致在合成事件和生命周期函數(shù)中無法立刻得到更新后的值囤锉,形成了異步的形式坦弟。說白了就是你無法立即獲取最新的值。也就是set后的值官地。下面看示例酿傍。
示例一
// css
//.active {
// color: red;
//}
// 父組件
function HooksDemo() {
// 定義一個(gè) tabList
const [tabList] = useState([
{
name: "全部",
code: "all",
},
{
name: "已開啟",
code: "open",
},
{
name: "已關(guān)閉",
code: "close",
},
]);
const [cutTab, setCutTat] = useState("");
useEffect(() => {
// 這里不要問為什么不初始化 useState("all"); 實(shí)際開發(fā)中這個(gè)值可能是其他組件傳過來的。
setCutTat("all");
console.log(cutTab,'看輸出'); // 輸出 ""
}, []);
return (
<div className="home">
<ul>
{tabList.map((tab, inx) => {
return <li key={inx} className={`${cutTab == tab.code ? 'active': ''}`}>{tab.name}</li>;
})}
</ul>
</div>
);
}
export default HooksDemo;
此時(shí)驱入,初始化的時(shí)候
console.log(cutTab,'看輸出'); // 輸出 ""
赤炒,但是頁面實(shí)際卻高亮第一個(gè)了。這就我上面說的它再執(zhí)行的過程實(shí)際是同步的亏较。只是由于機(jī)制問題莺褒,你輸出的時(shí)候他就變成異步了。那么此時(shí)會出現(xiàn)什么問題呢雪情?我們看下一個(gè)示例
示例二
import "./index.scss";
import { useState, useEffect } from "react";
// 父組件
function HooksDemo() {
// 定義一個(gè) tabList
const [tabList] = useState([
{
name: "全部",
code: "all",
},
{
name: "已開啟",
code: "open",
},
{
name: "已關(guān)閉",
code: "close",
},
]);
const [cutTab, setCutTat] = useState("");
useEffect(() => {
// 這里不要問為什么不初始化 useState("all"); 實(shí)際開發(fā)中這個(gè)值可能是其他組件傳過來的遵岩。
setCutTat("all");
console.log(cutTab, "看輸出"); // 輸出 ""
}, []);
// 頁簽切換
const tabChange = (tab: any) => {
if (tab.code == cutTab) {
return;
}
setCutTat(tab.code);
// 通過請求獲取數(shù)據(jù)
getListData();
};
// 請求獲取數(shù)據(jù)
const getListData = () => {
// 此時(shí)拿到的 code 其實(shí)是上一次的。
console.log(cutTab) // 輸出的是上一次的巡通。
};
return (
<div className="home">
<ul>
{tabList.map((tab, inx) => {
return (
<li
key={inx}
className={`${cutTab == tab.code ? "active" : ""}`}
onClick={(e) => tabChange(tab)}
>
{tab.name}
</li>
);
})}
</ul>
</div>
);
}
export default HooksDemo;
當(dāng)然有的朋友就要問了尘执,我為什么要setCutTat(tab.code);
后再掉接口,不能直接掉接口getListData(tab.code);
把參數(shù)傳過去嗎扁达?答案是可以的正卧。我這里只是演示某種特殊的需求蠢熄。
好跪解,入正題;此時(shí)點(diǎn)擊獲取的是上一次值,我總不能把上一次的值傳給后臺掉接口吧叉讥。下面就給大家提供下解決方案窘行。
四 初始useCallback,以及大概使用图仓。
useCallback返回一個(gè) memoized
回調(diào)函數(shù)罐盔。
把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項(xiàng)數(shù)組作為參數(shù)傳入 useCallback
,它將返回該回調(diào)函數(shù)的 memoized 版本救崔,該回調(diào)函數(shù)僅在某個(gè)依賴項(xiàng)改變時(shí)才會更新惶看。當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子組件時(shí),它將非常有用六孵。
更具體說明看官網(wǎng)介紹吧纬黎。
我這里就不多闡述了,直接講使用劫窒。
1. 新建 useSyncCallback.tsx
// useSyncCallback.tsx
import { useEffect, useState, useCallback } from "react";
const useSyncCallback = (callback: any) => {
const [proxyState, setProxyState] = useState({ current: false });
const [params, setParams] = useState([]);
const Func = useCallback(
(...args: []) => {
setParams(args);
setProxyState({ current: true });
},
[proxyState]
);
useEffect(() => {
if (proxyState.current === true) setProxyState({ current: false });
}, [proxyState]);
useEffect(() => {
proxyState.current && callback(...params);
});
return Func;
};
export default useSyncCallback;
2. 我的頁面引入本今,并使用它,替換掉原方法
import useSyncCallback from "./utils/useSyncCallback";
// 請求獲取數(shù)據(jù)
// const getListData = () => {
// // 此時(shí)拿到的 code 其實(shí)是上一次的主巍。
// console.log(cutTab) // 輸出的是上一次的冠息。
// };
const getListData = useSyncCallback(() => {
console.log(cutTab) // 此時(shí)就是最新的值了
})
今天的干貨就講到這了,下一篇講講父子通訊
孕索、兄弟通訊
逛艰、以及useEffect(()=>{},[n])
的基礎(chǔ)使用吧。