React Hook 遇到的小坑--持續記錄

1. 依賴項沒指定好

hook是利用閉包的特性來生成對應的方法;

當不傳依賴項,方法內部的狀態值都是取的在定義hook的初始值;
當傳入了依賴項,那麼依賴項值發生改變,hook會被更新,這個時候它內部用的變量也都會更新到最新。

所以,如果hook裏用了狀態變量,一定要記得作爲依賴項傳入,否則會遇到坑哦。
尤其是hook之間互相調用的時候,很容易指定不好依賴項。

如下例子:

本意:merchantChartQuery作爲fetchOrder的依賴項,控制fetchOrder的更新。當執行setCpsOrderStatus方法的時候,調用fetchOrder的方法,利用最新的數據來查詢接口。

結果:merchantChartQuery更新後,執行setCpsOrderStatus方法時,fetchOrder還是用了舊的數據去查詢接口。

// props裏傳入merchantChartQuery作爲查詢參數
const View: React.FunctionComponent = props => {
    // 從props裏取一個變量
	const { merchantChartQuery } = props;
	// 利用最新的merchantChartQuery數據去接口查詢數據
	const fetchOrder = useCallback(params => {
	     fetchMerchantOrderData({
	         page: 1,
	         ...merchantChartQuery,
	         ...params
	     });
	 }, [merchantChartQuery]);
	// 錯誤寫法
	// 這裏不寫依賴項時,雖然上面fetchOrder更新裏,
	// 但是這裏取得還是舊的fetchOrder,導致查詢時仍然用的就數據查詢
	const setCpsOrderStatus = useCallback(cpsOrderStatus => {
	     fetchOrder({ cpsOrderStatus });
	 }, []);
	 // 正確寫法
    const setCpsOrderStatus = useCallback(cpsOrderStatus => {
	     fetchOrder({ cpsOrderStatus });
	 }, [fetchOrder]);
}
  

2. hook利用Object.is來判斷依賴項是否更新

hook判斷一個依賴項是否發生更新,利用的是Object.is方法(內部是===來對比);
基本類型變量的比較基本不會有什麼困擾,引用類型就需要注意了。

如下例子:

本意:根據用戶的權限不用,reportTabs數組(控制頁面導航的顯示內容)也是不同的,希望在useEffect中通過權限查詢結果來更新數組內容。這樣不同權限的用戶會看到不同的導航內容。

結果:由於useState中使用了initReportTabs作爲初始值,獲得權限後更新時也是通過引用initReportTabs來處理的,導致Object.is在比較的時候,認爲變量沒有改變,不會更新組件,會導致頁面顯示與實際數據不符。

interface IReportTabTypes {
    key: string;
    title: string;
}
// reportTabs的初始值
const initReportTabs = [
    {
        key: 'goods-overview',
        title: '商品佣金報表',
    }
];
const View: React.FunctionComponent = () => {
	const [reportTabs, setReportTabs] = useState<IReportTabTypes[]>(initReportTabs); 
	useEffect(() => {
	    merchantApi.fetchIsTuanV2Header().then(res => {
	    // 引用類型的變量,指向的是同一個內存變量
	    const list = initReportTabs;
	    res && list.push({
	        key: 'merchant-overview',
	        title: '團長佣金報表',
	    });
	    // 無效寫法
	    setReportTabs(list);
	    // 有效寫法,傳入的是一個新的變量,hook認爲他是發生更新,進而會更新組件
	    setReportTabs([...list]);
	    });
	}, []);
}

3. hook內是一個閉包

如下例子:
本意:通過按鈕事件,控制彈框展示,彈框內是一個異步加載的級聯選擇框。
結果: 彈框裏的級聯選擇框內容並沒有隨着數據的更新而實時展示出來

const View: React.FunctionComponent = () => {
	// 級聯選擇框的選項列表
	const [categoryList, setCategoryList] = useState<any[]>([]);
	// 級聯選擇框異步加載的方法
	const onLoadMore = useCallback(option => {
	    option.loading = true;
	    // 異步查詢子類數據,更新categoryList特定項的children數組內容
	    api.listChildren({
	      pid: option.id,
	      channel: 1,
	    }).then(data => {
	      option.children = data.map(item => {
	        return {
	          ...item,
	          isLeaf: true,
	        };
	      });
	    }).finally(() => {
	      option.loading = false;
	      const list = [ ...categoryList ];
	      setCategoryList(list);
	    });
	  }, [categoryList]);
	  // 調用handleOpenDialog方法後,創建一個彈框,這個時候的彈框是在一個閉包中,外部數據更新只是能引起handleOpenDialog方法的更新,彈框中的狀態無法被改變
	  // 所以雖然接口請求到了新的數據,categoryList發生了更新,但是級聯框的內容依然是舊的
	const handleOpenDialog = useCallback(() => {
		Dialog.open({
			title: '選擇類目',
			content: <Cascader
		                 filterable={true}
		                 value={item.ids}
		                 options={categoryList}
		                 loadMore={onLoadMore}
		                 propsAlias={{label: 'name', id: 'id'}}
		            />
			footer: 
		})
	}, [categoryList]);

	return (
		<div>
			<button onClick={handleOpenDialog}>顯示彈窗</button>
		</div>
	)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章