محبوب ترین هوک های React را میشناسید؟ و آیا با آن ها چقدر آشنا هستید؟! هوک ها به سادگی توابعی هستند که به شما امکان می دهند به ویژگی های React متصل شوید یا از آنها استفاده کنید. آنها در React Conf 2018 برای رسیدگی به سه مشکل اصلی کامپوننت های کلاس معرفی شدند: wrapper hell، کامپوننت های عظیم و کلاس های گیج کننده. هوک ها به کامپوننت های تابعی React قدرت می دهند و توسعه یک برنامه کامل را با آن ممکن می سازند.
مسائل فوق الذکر کامپوننت های کلاس به هم متصل هستند و حل یکی بدون دیگری می تواند مشکلات بیشتری را ایجاد کند. خوشبختانه، هوکها همه مشکلات را به سادگی و کارآمد حل کردند و در عین حال فضایی برای ویژگیهای جالبتر در React ایجاد کردند. هوک ها جایگزین مفاهیم و کلاس های موجود React نمی شوند، آنها فقط یک API برای دسترسی مستقیم به آنها ارائه می دهند.
تیم React چندین هوک را در React 16.8 معرفی کرد. با این حال، می توانید از هوک های ارائه دهندگان شخص ثالث نیز در برنامه خود استفاده کنید یا حتی یک هوک سفارشی ایجاد کنید. در این مقاله، نگاهی به چند هوک مفید در React و نحوه استفاده از آنها خواهیم داشت. ما چندین نمونه کد از هر هوک را بررسی خواهیم کرد و همچنین یک سری تغییرات در ری اکت 19 صورت گرفته است که می توانید با استفاده از این مقاله با آن ها آشنا شوید.
کنوانسیون و قوانین هوک ها
قبل از پرداختن به هوک های مختلف، نگاهی به کنوانسیون و قوانین مربوط به آنها می تواند مفید باشد. در اینجا برخی از قوانینی که در مورد هوک اعمال می شود آورده شده است.
نامگذاری هوک ها باید با استفاده از پیشوند شروع شود. بنابراین، میتوانیم useState
، useEffect
و غیره داشته باشیم. اگر از ویرایشگرهای کد مدرن مانند Atom و VSCode استفاده میکنید، افزونه ESLint میتواند یک ویژگی بسیار مفید برای هوک های React باشد. این افزونه هشدارها و نکات مفیدی در مورد بهترین شیوه ها ارائه می دهد.
هوک ها باید در سطح بالای یک کامپوننت، قبل از دستور return
فراخوانی شوند. آنها را نمی توان در داخل یک دستور شرطی، حلقه یا توابع تو در تو فراخوانی کرد.
هوک ها باید از یک تابع React (در داخل یک کامپوننت React یا یک هوک دیگر) فراخوانی شوند. نباید از تابع Vanilla JS فراخوانی شود.
1. هوک useState
یک "متغیر استیت" ایجاد می کند که کامپوننت را در هنگام تغییر به روز می کند. useState رایج ترین هوک در React است. هوک برای ایجاد استیت به 3 ورودی نیاز دارد.
Current state (count)
: نام متغیر که برابر با وضعیت فعلی است.
تابع (setCount
): تابعی که برای تغییر استیت فراخوانی می شود.
مقدار اولیه (0
): مقدار پیش فرض هنگام تنظیم اولیه صفحه.
import { useState } from "react";
const App = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Add 1 to count</button>
</div>
);
};
2.هوک useEffect
تابعی که هر بار که ویو mounted می شود، یا زمانی که وضعیت داخل []
تغییر می کند، فراخوانی می شود. useEffect اغلب در React استفاده می شود. یکی از نکات کلیدی که هنگام استفاده از useEffect باید به آن توجه کرد، پارامتر دوم است: []
. تفاوت بزرگی بین خالی گذاشتن کروشه های مربع یا وارد کردن استیت (ها) وجود دارد.
[]Empty
: این بدان معناست که ()useEffect
فقط یک بار پس از mounted فراخوانی می شود.Filled [count]
: به این معنی است که ()useEffect
در هنگام mounted فراخوانی می شود، و زمانی که استیت (count) تغییر می کند.
import { useEffect, useState } from "react";
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("View Mounted");
}, []);
useEffect(() => {
console.log("View Mounted or Count updated");
}, [count]);
// .. //
};
برای آشنایی با Life Cycle هوک ها این مقاله را بررسی کنید.
3. هوک useRef
هوکی که میتواند یک مقدار قابل تغییر را ذخیره کند، که ویو را در بهروزرسانی و رندر نکند. همچنین، می توان از آن برای ذخیره عناصر DOM استفاده کرد. useRef یک شی را برمی گرداند که می تواند در یک برنامه باقی بماند. هوک فقط یک property دارد، current، و به راحتی میتوانیم آرگومان را به آن منتقل کنیم.
رایج ترین استفاده از useRef، ذخیره یک عنصر است که می توان به آن در داخل کامپوننت دسترسی پیدا کرد. این می تواند هنگام ایجاد یک فیلد input مفید باشد، جایی که شما به عنوان مثال. نیاز به دسترسی به مقدار دارید.
import { useRef } from "react";
const App = () => {
const inputRef = useRef();
return (
<input
ref={inputRef}
onChange={() => {
console.log(inputRef.current.value);
}}
/>
);
};
4. هوک useMemo
هوکی که فقط هر بار که استیت خاصی تغییر می کند فراخوانی می شود. useMemo بسیار شبیه به useEffect عمل می کند، زیرا فقط هر بار که استیت درون براکت های مربع تغییر می کند، فراخوانی می شود. این می تواند هنگام فراخوانی یک API مفید باشد، که نباید هر بار که کامپوننت به روز می شود، رندر شود. هوک useMemo برای به خاطر سپردن محاسبات گران قیمت طراحی شده است. Memoization به سادگی به معنای ذخیره سازی است. نتیجه محاسبات را با توجه به مقادیر وابستگی در حافظه پنهان ذخیره می کند تا زمانی که همان مقادیر ارسال می شود، useMemo فقط مقدار محاسبه شده قبلی را بدون محاسبه مجدد دوباره بیرون می اندازد. این می تواند به طور قابل توجهی عملکرد را در صورت انجام صحیح بهبود بخشد.
const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])
بیایید سه مورد از هوک useMemo را در نظر بگیریم.
هنگامی که مقادیر وابستگی، a و b ثابت می مانند.
هوک useMemo مقدار ذخیره شده قبلی محاسبه شده را بدون محاسبه مجدد برمی گرداند.
هنگامی که مقادیر وابستگی، a و b تغییر می کنند.
هوک مقدار را دوباره محاسبه می کند.
هنگامی که هیچ مقدار وابستگی منتقل نمی شود.
هوک مقدار را دوباره محاسبه می کند.
مثال :
import { useState, useMemo } from "react";
const App = () => {
const [apiURL, setApiURL] = useState("https://callAPI/");
const apiResults = useMemo(() => {
callAPI();
}, [apiURL]);
return (
<div>
<button onClick={() => setApiURL("https://dummyAPI")}>Change API Url</button>
</div>
);
};
const callAPI = () => {
console.log("Call API");
};
البته دقت کنید استفاده بیش از حد یا بیهوده از این هوک می تواند بر روی عملکرد تاثیر منفی داشته باشد، برای اینکه بدانید که چه زمانی نباید از هوک useMemo استفاده کنید می توانید این مقاله را بررسی کنید.
5.هوک useCallback
دقیقاً مانند useMemo عمل می کند، اما به جای برگرداندن یک مقدار، یک تابع را برمی گرداند. useCallback دقیقاً مانند useMemo عمل می کند. با این حال به جای فراخوانی تابع در هنگام تغییر استیت ، تابعی را تنظیم می کند که پس از آن قابل فراخوانی می شود.
این می تواند زمانی مفید باشد که نیاز به ارسال props
به تابع باشد.
// Returns the value 100
const apiResults = useMemo(() => {
return 100
}, [apiURL]);
// Returns a function, which then can be called
const getApiResults = useCallback((value) => {
return 100 + value
}, [apiURL]);
برای آشنایی با تفاوت بین دو هوک useMemo و useCallback این مقاله را بررسی کنید.
6. هوک useContext
استیتی ایجاد می کند که در همه کامپوننت ها قابل دسترسی است. در این مثال ما به نحوه به اشتراک گذاری یک متغیر استیت بین دو کامپوننت نگاه می کنیم که اعلام می کند آیا برنامه دارای تم dark است. به طور معمول شما باید این اطلاعات را از طریق ابزارها منتقل کنید، اما این امر هنگام کار با چندین کامپوننت بسیار سخت خواهد بود.
بنابراین ما میتوانیم از useContext
استفاده کنیم که یک متغیر استیت ایجاد میکند، که میتوان از همه کامپوننتها بدون نیاز به props به آن دسترسی داشت.
// App.jsx //
import { useState, createContext } from "react";
export const ThemeContext = createContext(null);
const App = () => {
const [isDarkThemed, setIsDarkThemed] = useState(false);
return (
<ThemeContext.Provider value={isDarkThemed}>
<button onClick={() => setIsDarkThemed((prev) => !prev)}>
Change Theme
</button>
<AboutPage />
</ThemeContext.Provider>
);
};
export default App;
سپس میتوانیم مقدار isDarkThemed
را در AboutPage.jsx
واکشی کنیم.
// AboutPage.jsx //
import { useContext } from "react";
import { ThemeContext } from "./App";
const AboutPage = () => {
const isDarkTheme = useContext(ThemeContext);
return (
<div style={{ backgroundColor: isDarkTheme ? "black" : "white" }}>
// .. //
</div>
);
};
برای آشنایی با هوک useTransition این مقاله را بررسی کنید.
7. هوک useReducer
شبیه useState
عمل می کند، اما به جای داشتن یک مقدار واحد، حاوی یک شی است.
تفاوت بین useState
و useReducer
:
useState
فقط می تواند یک مقدار را ذخیره کند، به عنوان مثال. "0".useReducer
می تواند یک شی را ذخیره کند که می تواند چندین مقدار داشته باشد. همچنین، دارای تابع dispatch
است که عملکرد پیشرفته تر را ساده می کند.useReducer
باید زمانی استفاده شود که یک شی دارید که دارای چندین مقدار استیت است. به عنوان مثال. هنگام ذخیره یک شی کاربر که دارای نام، ایمیل و شماره است.
سپس داشتن یک useReducer
در مقایسه با 3 useState
برای هر مقدار کاربر ساده تر است.
function reducer(state, action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
const App = () => {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<h1>{state.count}</h1>
<button onClick={() => dispatch({ type: "increment" })}>Increase</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrease</button>
</div>
);
};
8. هوک useLayoutEffect
شبیه useEffect
کار می کند، اما در عوض قبل از مونت view فراخوانی می شود. مانند هوک useEffect
این هوک useLayoutEffect
پس از mounted و رندر شدن کامپوننت فراخوانی می شود. این هوک پس از جهش DOM صورت می گیرد و این کار را به صورت همزمان انجام می دهد. به غیر از فراخوانی همزمان پس از جهش DOM، useLayoutEffect
همان کاری را انجام می دهد که useEffect
انجام می دهد.
useLayoutEffect
فقط باید برای انجام جهش DOM یا اندازه گیری مربوط به DOM استفاده شود، در غیر این صورت، باید از هوک useEffect
استفاده کنید. استفاده از هوک useEffect
برای توابع جهش DOM ممکن است باعث برخی مشکلات عملکرد مانند flickering شود، اما useLayoutEffect
به خوبی آنها را کنترل می کند، زیرا پس از وقوع جهش اجرا می شود.
تفاوت بین useEffect
و useLayoutEffect
:
useEffect
زمانی که ویو ظاهر می شود یا mounted می شود فراخوانی می شود.useLayoutEffect
قبل از اینکه ویو ظاهر شود یا mounted شود فراخوانی می شود.
این می تواند زمانی مفید باشد که نیاز به نشان دادن لیستی از موارد فوراً روی صفحه نمایش باشد، بدون اینکه باعث چشمک زدن/فلاش کوچک شود.
دقت کنید که اسناد رسمی React میگویند: «useLayoutEffect
میتواند به عملکرد آسیب برساند. در صورت امکان useEffect را ترجیح دهید."
import { useLayoutEffect} from "react";
const App = () => {
useLayoutEffect(() => {
console.log("View has not mounted yet");
}, []);
// .. //
};
9. هوک های UseDispatch و useSelector
useDispatch یک هوک Redux برای ارسال (راهاندازی) اکشن در یک برنامه است. یک شی اکشن را به عنوان آرگومان می گیرد و اکشن را فراخوانی می کند. useDispatch
معادل هوک با mapDispatchToProps
است. برای آشنایی با ریداکس این مقاله را بررسی کنید.
از طرف دیگر useSelector
یک هوک Redux برای ارزیابی استیت های Redux است. برای انتخاب دقیق ریدیوسر Redux از فروشگاه تابعی لازم است و سپس استیت های مربوطه را برمی گرداند.
هنگامی که فروشگاه Redux ما از طریق ارائه دهنده Redux به یک برنامه React متصل شد، می توانیم اکشن ها را با useDispatch
فراخوانی کنیم و با useSelector
به حالت ها دسترسی پیدا کنیم. هر اکشن و استیت Redux را می توان با این دو هوک ارزیابی کرد.
توجه داشته باشید که این استیت ها با React Redux ارسال می شوند. آنها در کتابخانه اصلی Redux در دسترس نیستند. استفاده از این هوک ها بسیار ساده است. ابتدا باید تابع dispatch
را اعلام کنیم و سپس آن را فعال کنیم.
import {useDispatch, useSelector} from 'react-redux'
import {useEffect} from 'react'
const myaction from '...'
const ReduxHooksExample = () =>{
const dispatch = useDispatch()
useEffect(() => {
dispatch(myaction());
//alternatively, we can do this
dispatch({type: 'MY_ACTION_TYPE'})
}, [])
const mystate = useSelector(state => state.myReducerstate)
return(
...
)
}
export default ReduxHooksExample
در کد بالا، useDispatch
و useSelector
را از react-redux
وارد کردیم. سپس، در یک هوک useEffect
، اکشن را ارسال کردیم. میتوانیم اکشن را در فایل دیگری تعریف کنیم و سپس آن را در اینجا فراخوانی کنیم یا میتوانیم آن را مستقیماً همانطور که در فراخوانی useEffect
نشان داده شده است تعریف کنیم.
هنگامی که اقدامات را ارسال کردیم، استیت های ما در دسترس خواهند بود. سپس میتوانیم استیت را با استفاده از هوک useSelector
مطابق شکل بازیابی کنیم. حالت ها را می توان به همان روشی که از استیت های هوک useState
استفاده می کنیم استفاده می شود.
بیایید برای نشان دادن این دو هوک به یک مثال نگاه کنیم.
برای نشان دادن این مفهوم، باید یک فروشگاه Redux، ریدیوسر و اکشن ایجاد کنیم. برای ساده کردن کارها در اینجا، از کتابخانه Redux Toolkit با پایگاه داده جعلی خود از JSONPlaceholder
استفاده می کنیم.
برای شروع باید پکیج های زیر را نصب کنیم.
npm i redux @reduxjs/toolkit react-redux axios
ابتدا، اجازه دهید staffsSlice.js
را برای مدیریت ریدیوسر و اکشن برای API کارمندان (یا کاربران) خود ایجاد کنیم.
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const endPoint = "https://api.anophel.com/api/v1/users";
export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => {
const { data } = await axios.get(endPoint);
return data;
});
const employeesSlice = createSlice({
name: "employees",
initialState: { employees: [], loading: false, error: "" },
reducers: {},
extraReducers: {
[fetchEmployees.pending]: (state, action) => {
state.status = "loading";
},
[fetchEmployees.fulfilled]: (state, action) => {
state.status = "success";
state.employees = action.payload;
},
[fetchEmployees.rejected]: (state, action) => {
state.status = "error";
state.error = action.error.message;
}
}
});
export default employeesSlice.reducer;
این تنظیم استاندارد برای جعبه ابزار Redux است. ما از createAsyncThunk
برای دسترسی به میدلور Thunk
برای انجام اکشن های async
استفاده کردیم. این به ما امکان داد تا لیست کارمندان را از API دریافت کنیم. سپس employsSlice
را ایجاد کردیم و بسته به نوع عملکرد، «بارگذاری»، «خطا» و دادههای کارکنان را برگرداندیم.
شما نیز می توانید به صورت رایگان از API های آنوفل استفاده کنید برای آشنایی بیشتر می توانید در آنوفل ثبت نام کنید و وارد پنل کاربری شده و در آنجا از API های مختلف آنوفل همراه با مثال استفاده کنید.
جعبه ابزار Redux نیز راه اندازی فروشگاه را آسان می کند. اینجا فروشگاه است.
import { configureStore } from "@reduxjs/toolkit";
import { combineReducers } from "redux";
import employeesReducer from "./employeesSlice";
const reducer = combineReducers({
employees: employeesReducer
});
export default configureStore({ reducer });;
در اینجا، ما از combinationReducers
برای بستهبندی ریدیوسر ها و تابع configureStore
ارائهشده توسط Redux toolkit برای راهاندازی فروشگاه استفاده کردیم.
بیایید به استفاده از این در برنامه خود ادامه دهیم.
ابتدا باید Redux را به برنامه React خود متصل کنیم. در حالت ایده آل، این باید در ریشه برنامه ما انجام شود. من دوست دارم این کار را در فایل index.js
انجام دهم.
import React, { StrictMode } from "react";
import ReactDOM from "react-dom";
import store from "./redux/store";
import { Provider } from "react-redux";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<Provider store={store}>
<StrictMode>
<App />
</StrictMode>
</Provider>,
rootElement
);
در اینجا، من فروشگاهی را که در بالا ایجاد کردم و همچنین Provider
را از react-redux
وارد کرده ام. سپس، کل برنامه را با Provider
پوشش دادیم و فروشگاه را به آن منتقل کردم. این باعث می شود فروشگاه در سراسر برنامه ما در دسترس باشد.
سپس میتوانیم از هوکهای useDispatch
و useSelector
برای واکشی دادهها استفاده کنیم.
بیایید این کار را در فایل App.js
خود انجام دهیم.
import { useDispatch, useSelector } from "react-redux";
import { fetchEmployees } from "./redux/employeesSlice";
import { useEffect } from "react";
export default function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchEmployees());
}, [dispatch]);
const employeesState = useSelector((state) => state.employees);
const { employees, loading, error } = employeesState;
return (
<div className="App">
{loading ? (
"Loading..."
) : error ? (
<div>{error}</div>
) : (
<>
<h1>List of Employees</h1>
{employees.map((employee) => (
<div key={employee.id}>
<h3>{`${employee.firstName} ${employee.lastName}`}</h3>
</div>
))}
</>
)}
</div>
);
}
در کد بالا، از هوک useDispatch
برای فراخوانی اکشن fetchEmployees
ایجاد شده در فایل workingslice.js
استفاده کردیم. این باعث می شود که کارمندان در برنامه ما در دسترس باشند. سپس از هوک useSelector
برای بدست آوردن استیت ها استفاده کردیم. پس از آن، ما نتایج را با map
از طریق کارمندان نمایش دادیم.
10. هوک use
استفاده از هوک در حال حاضر فقط در کانالهای Canary و آزمایشی React موجود است و در React 19 در درسترس خواهد بود. use یک React API است که به شما امکان میدهد مقدار منبعی مانند Promise یا context را بخوانید.
const value = use(resource);
برای خواندن مقدار منبعی مانند Promise یا context، استفاده از کامپوننت خود را فراخوانی کنید.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...
برخلاف React Hooks، این هوک را می توان در حلقه ها و دستورات شرطی مانند if فراخوانی کرد. مانند React Hooks، تابعی که فراخوانی استفاده می کند باید یک Component یا Hook باشد.
هنگامی که با یک Promise فراخوانی می شود، API use با Suspense و error boundaries یکپارچه می شود. کامپوننت فراخوانی use به حالت تعلیق در می آید در حالی که Promise تصویب شده برای استفاده در حالت تعلیق است. اگر کامپوننت ای که استفاده را فراخوانی می کند در یک مرز Suspense پیچیده شده باشد، return نمایش داده می شود. هنگامی که Promise حل شد، کامپوننت های رندر شده با استفاده از داده های برگردانده شده توسط use API جایگزین Suspense بازگشتی می شود. اگر Promise تصویب شده برای استفاده رد شود، return نزدیکترین مرز خطا نمایش داده می شود.
نتیجه
در این مقاله، نگاهی به چند هوک مفید React انداختیم که در اکثر برنامه های خود از آنها استفاده خواهید کرد. ما آنچه را که آنها ارائه می دهند و نحوه استفاده از آنها در برنامه شما را بررسی کردیم. ما همچنین چندین نمونه کد را بررسی کردیم تا به شما در درک این هوک ها و اعمال آنها در برنامه خود کمک کند.
من شما را تشویق می کنم که این هوک ها را در برنامه خود امتحان کنید تا آنها را بیشتر درک کنید.