Hook是React16.8開始新增的特性。雖然React官方文檔已經做出了針對React hooks的相關概念的講解,可是光看官方文檔是很難將hooks使用好的,在編寫hooks的過程當中很容易跳進陷阱和錯誤。本文總結了5個很差的地方。react
useState
在函數組件中咱們可使用useState
來管理狀態,這使得對狀態的管理變得很簡單,可是也容易被濫用,咱們經過下面的代碼樣例看下容易忽略的地方。api
不推薦×數組
function ClickButton(props){
const [count, setCount] = useState(0)
const onClickCount = () => {
setCount((c) => c + 1)
}
const onClickRequest = () => {
apiCall(count)
}
return (
<div> <button onClick={onClickCount}>Click</button> <button onClick={onClickRequest}>Submit</button> </div>
)
}
複製代碼
問題所在:仔細看上面的代碼,乍一看其實也沒什麼問題,點擊按鈕更新 count
。可是問題也就出在這裏,咱們的 return
部分並無用到 count 狀態,而每次 setCount
都會使組件從新渲染一次,而這個渲染並非咱們須要的,多出來的渲染會使得頁面的性能變差,所以咱們能夠改造一下代碼,以下代碼:markdown
推薦√
若是咱們只是單純的想要一個能在組件聲明週期內保存的變量,可是變量的更新不須要組件的從新渲染,咱們可使用 useRef
鉤子。網絡
function ClickButton(props){
const count = useRef(0)
const onClickCount = () => {
count.current++
}
const onClickRequest = () => {
apiCall(count.current)
}
return (
<div> <button onClick={onClickCount}>Click</button> <button onClick={onClickRequest}>Submit</button> </div>
)
}
複製代碼
router.push
而非link
在React SPA應用中,咱們用react-router
來處理路由的跳轉,咱們很常常在組件中寫了一個按鈕,經過點擊按鈕的事件來處理路由的跳轉,以下代碼:react-router
不推薦×框架
function ClickButton(props){
const history = useHistory()
const onClickGo = () => {
history.push('/where-page')
}
return <button onClick={onClickGo}>Go to where</button>
}
複製代碼
問題所在:儘管上述代碼能夠正常工做,可是卻不符合Accessibility(易訪問性設計)的要求,此類按鈕並不會被屏幕閱讀器看成一個能夠跳轉的連接。所以咱們能夠改造一下代碼,以下代碼:異步
推薦√函數
function ClickButton(props){
return <Link to="/next-page"> <span>Go to where</span> </Link>
}
複製代碼
useEffect
來處理actions有時候,咱們只想在 React 更新 DOM 以後運行一些額外的代碼。好比發送網絡請求,手動變動 DOM,記錄日誌。oop
不推薦×
function DataList({ onSuccess }) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const fetchData = () => {
setLoading(true);
callApi()
.then((res) => setData(res))
.catch((err) => setError(err))
.finally(() => setLoading(false));
};
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
if (!loading && !error && data) {
onSuccess();
}
}, [loading, error, data, onSuccess]);
return <div>Data: {data}</div>;
}
複製代碼
問題所在:上面的代碼使用了兩個useEffect
,第一個用來請求異步數據,第二個用來調用回調函數。在第一個異步請求數據成功,纔會觸發第二個 useEffect
的執行,可是,咱們並不能徹底保證,第二個 useEffect
的依賴項徹底受控於第一個 useEffect
的成功請求數據。所以咱們能夠改造一下代碼,以下代碼:
推薦√
function DataList({ onSuccess }) {
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);
const fetchData = () => {
setLoading(true);
callApi()
.then((res) => {
setData(res)
onSuccess()
})
.catch((err) => setError(err))
.finally(() => setLoading(false));
};
useEffect(() => {
fetchData();
}, []);
return <div>Data: {data}</div>;
}
複製代碼
何時該把一個組件分紅幾個更小的組件?如何構建組件樹?在使用基於組件的框架時,全部這些問題天天都會出現。然而,設計組件時的一個常見錯誤是將兩個用例組合成一個組件。
不推薦×
function Header({ menuItems }) {
return (
<header> <HeaderInner menuItems={menuItems} /> </header>
);
}
function HeaderInner({ menuItems }) {
return isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />;
}
複製代碼
問題所在:上面的代碼經過這種方法,組件HeaderInner
試圖同時成爲兩個不一樣的東西,一次作不止一件事情並非很理想。此外,它還使得在其餘地方測試或重用組件變得更加困難。所以咱們能夠改造一下代碼,以下代碼:
推薦√
將條件提高一級,能夠更容易地看到組件的用途,而且它們只有一個職責,即<Tabs/>
或<BurgerButton/>
,而不是試圖同時成爲兩個不一樣的東西。
function Header(props) {
return (
<header> {isMobile() ? <BurgerButton menuItems={menuItems} /> : <Tabs tabData={menuItems} />} </header>
)
}
複製代碼
經過對比componentWillReceiveProps
或componentDidUpdate
方法,才認識到userEffect
的美麗。可是沒有穩當使用useEffect也是容易出問題的。
不推薦×
function Example(props) {
const location = useLocation();
const fetchData = () => {
/* Calling the api */
};
const updateBreadcrumbs = () => {
/* Updating the breadcrumbs*/
};
useEffect(() => {
fetchData();
updateBreadcrumbs();
}, [location.pathname]);
return (
<div> <BreadCrumbs /> </div>
);
}
複製代碼
問題所在:上面的useEffect
同時觸發了兩個反作用,可是並不都是咱們須要的反作用,所以咱們能夠改造一下代碼,以下代碼:
推薦√
將兩個反作用從一個useEffect中分離出來。
function Example(props) {
const location = useLocation();
const fetchData = () => {
/* Calling the api */
};
const updateBreadcrumbs = () => {
/* Updating the breadcrumbs*/
};
useEffect(() => {
updateBreadcrumbs();
}, [location.pathname]);
useEffect(()=>{
fetchData();
},[])
return (
<div> <BreadCrumbs /> </div>
);
}
複製代碼
參考:Five common mistakes writing react components (with hooks) in 2020