useEffect – The Hook React هرگز نباید ارائه می شد

React زمانی یک کتابخانه خارق العاده بود. یادگیری سریع، سفارشی کردن آسان و نوشتن کدهای تمیز را برای شما آسان کرده است. حداقل در زمانی که برای اولین بار کتابخانه را با قلاب به جای اجزای کلاس قهوه ای قدیمی رنگ سفید کردند، اینطور بود.
وقتی React کتابخانه را دوباره رنگ کرد، آنها اشتباه کردند. آنها یک قلم مو قرمز را در سطل سفید گذاشتند که اکنون از آن برای رنگ آمیزی کل کتابخانه به صورت فاجعه صورتی استفاده می کنند. React آینده خود را با React 18 دوباره رنگ آمیزی کرد، اما در آن تصویر، قلاب useEffect متعلق به آن نیست.
من فکر می کنم زمان آن رسیده است که از شر آن قلاب خطرناک و بحث برانگیز خلاص شوید، اما تا آن زمان، من به شما نکاتی را در مورد نحوه استفاده از آن بدون رنگ صورتی در سراسر کدتان ارائه خواهم کرد.
در این مقاله
useEffect – برس قرمز در سطل سفید
React یک قلاب useEffect فوق العاده به سطل قلاب های خود اضافه کرده است. تنها مشکل این است که به آنجا تعلق ندارد. زمانی که React 18 برای اولین بار آمد، وب با هشدارهایی در مورد useEffect منفجر شد و ادعا کرد که نباید از آن استفاده شود و ما نباید در چرخه زندگی به React فکر کنیم.
حقیقت این است که useEffect مدت ها قبل از آن یک مشکل بود. زمانی که توسعه دهندگان قصد داشتند یک آرایه وابستگی خالی داشته باشند، بسیار رایج بود که میتوانستیم ببینیم که توسعهدهندگان آرایه وابستگی را کنار میگذارند، که باعث میشود useEffect به طور موثر بیفایده باشد.
و از آنجایی که متاسفانه React بخش زیادی از بهینه سازی کد را به توسعه دهنده واگذار می کند، ممکن است لازم باشد کد خود را با استفاده از useCallback بهینه کنید، در غیر این صورت می توانید در حلقه های رندر بی نهایت گیر کنید. یا به روشی مشابه، ممکن است این اتفاق بیفتد که کد شما در هر رندر اجرا شود.
const ExampleComponent = () => {
// This function will be triggered on every render.
const getData = () => {
// Get some data from server.
}
useEffect(() => {
getData()
}, [getData]);
return <div>Example<div>
}
const ExampleComponent = () => {
// Solution is to optimize the code with a useCallback...
const getData = useCallback(() => {
// Get some data from server.
}, [])
useEffect(() => {
// We could also place the getData function in the useEffect,
// with the drawback of readability if the function is big.
// const getData = () => { ... }
getData()
}, [getData]);
return <div>Example<div>
}
در روزهای قبل، مقالات زیادی در مورد اینکه چرا همیشه باید همه وابستگیها را به useEffect خود اضافه کنید، وجود داشت، حتی زمانی که نمیخواهید زمانی که برخی از وابستگیها تغییر میکنند، افکت اجرا شود. که منجر به هک های زشت با عبارات if برای جلوگیری از اجرای کد می شود.
import { useEffect } from 'react'
const Component = ({ someOtherDependency }) => {
const [value, setValue] = useState(null)
useEffect(() => {
// A common seen if statement added just to
// prevent the effect from running.
if (value === null) {
setValue("someValue")
}
}, [value, someOtherDependency])
return <div>Example<div>
}
جایگزین های دیگر تمیزتر بودند، اما فقط در موارد معدودی قابل استفاده بودند. مثال زیر ترفندی را برای جلوگیری از افزودن متغیر حالت به آرایه وابستگی نشان می دهد.
import { useEffect } from 'react'
const Component = () => {
const [count, setCount] = useState(0)
useEffect(() => {
// By passing a function to setCount, we can access its old
// value without adding count to the dependency array.
setCount(oldCount => oldCount + 1)
}, [])
return <div>Example<div>
}
React به نقاشی با برس قرمز ادامه می دهد
اگر استفاده از useEffect در React 17 به اندازه کافی دشوار نبود، React 18 آن را حتی بیشتر دردسرساز می کرد. مشکل اصلی قلاب امروزی زشت نیست اگر عبارات، وابستگی های از دست رفته یا این واقعیت است که شما باید همه وابستگی ها را بدون توجه به اینکه بخواهید یا نه به آرایه وابستگی اضافه کنید. واقعیت این است که حتی اگر همه این کارها را انجام دهید، به دلیل رندر همزمان، useEffect شما ممکن است چندین بار اجرا شود.
خوشبختانه، سازندگان React متوجه شدند که حالت همزمان باعث نگرانیهای زیادی شده است، بنابراین آنها آن را تنها با فعال کردن آن در بخشهایی از کد شما که از ویژگیهای جدید React 18 استفاده میکند، حل کردند. من می گویم یک راه حل بسیار خوب. بسیار.
مشکل اینجاست که React Docs برای useEffect بسیار گسترده است. هنگام انتخاب ویژگیهای جدید، و در نتیجه روشن کردن حالت همزمان، اسناد مربوط به useEffect پیچیدهتر میشوند. یک صفحه توصیف useEffect کافی نیست، شما به یک صفحه طولانی دیگر نیاز دارید، و دیگری، و دیگری، و دیگری…
شما باید فکر کنید که آیا حالت همزمان فعال است یا خیر. شما باید به تمام مشکلات فکر کنید. شما باید به تمام بهینه سازی ها فکر کنید. باید به این فکر کنید که بهترین تمرین چیست. شما باید به نحوه رندر کامپوننت فکر کنید، حتی اگر تشویق شوید که به روشهای چرخه عمر کلاس قدیمی مانند componentDidMount و componentDidUpdate فکر نکنید.
React دیگر چارچوبی نیست که کد یادگیری پایینی داشته باشد. شروع ساختن با آن سریع است، اما زمان زیادی طول می کشد تا یاد بگیرید چگونه آن را درست بنویسید بدون اینکه اشکالات زیادی ایجاد کنید.
نمایش بصری React. آیا می توانید useEffect را تشخیص دهید؟
روش صحیح استفاده از useEffect
React در حال دریافت بسیاری از ویژگی های خوب جدید است. در همین حال، useEffect روز به روز خطرناک تر می شود. اگر به شرح مسائل مربوط به useEffect ادامه دهم، این مقاله بسیار طولانی خواهد شد، فقط به میزان مستندات بزرگی برای آن نگاه کنید. به همین دلیل، من به شما یک نکته برای نحوه مدیریت useEffect می دهم.
Let useEffect render.
با اجازه دادن به آن رندر، منظورم این نیست که شما داوطلبانه باید اجازه دهید در هر رندر رندر شود. منظور من این است که نباید از اینکه اجازه دهید بارها رندر شود بترسید. همه وابستگی ها را به آرایه وابستگی آن اضافه کنید و اجازه دهید هر بار که اجرا می شود کار خود را انجام دهد.
کاری که نباید انجام دهید، جلوگیری از اجرای اثر در موارد استفاده خاص است. کد زیر افتضاح است و به شدت مستعد اشکال است.
const getDataFromBackend = () => {
// Some code.
}
const ExampleComponent = ( {
someVariable,
anotherVariable,
thirdVariable
}) => {
useEffect(() => {
if (someVariable === null) {
if (anotherVariable > 10
&& (thirdVariable !== undefined || thirdVariable !== null)
) {
getDataFromBackend()
}
}
}, [someVariable, anotherVariable, thirdVariable]);
return <div>Example<div>
}
کد زشت بالا باید شبیه این باشد:
const getDataFromBackend = () => {
// Some code.
}
const ExampleComponent = ( {
someVariable,
anotherVariable,
thirdVariable
}) => {
useEffect(() => {
getDataFromBackend()
}, [someVariable, anotherVariable, thirdVariable]);
return <div>Example<div>
}
ولی؟ ما نمیتوانیم با درخواستهای شبکه فراوان اسپم پشتیبان را ارسال کنیم؟ آیا می توانیم؟
نه، شما نباید باطن را اسپم کنید. کاری که باید انجام دهید این است که مطمئن شوید getDataFromBackend فقط در صورت لزوم داده ها را واکشی می کند. نه با کمک دستور if، بلکه با استفاده از حافظه پنهان، debounce یا throttling.
قلابهایی مانند useSWR، useQuery و RTK Query چنین مواردی را برای شما انجام میدهند، با مقداری نیاز به تنظیمات. استفاده از قلاب هایی مانند آن امروزه کاملاً ضروری است، نه تنها به دلیل مشکلات useEffect، بلکه به این دلیل که آنها منطق زیادی را در بر می گیرند که در غیر این صورت باید خودتان با تلاش های مجدد و مدیریت وضعیت پیاده سازی کنید.
اهمیت بیتوانی
در عنوان قبلی میتوانیم نحوه واکشی صحیح دادهها را در useEffect ببینیم. من همچنین ادعا کردم که باید از اجرای useEffect جلوگیری کنید. در برخی موارد، ممکن است این امکان پذیر نباشد. گاهی اوقات ما نمیتوانیم از حافظه پنهان یا debounce استفاده کنیم، این ممکن است در هنگام ارسال درخواستهای POST به backend رخ دهد.
درخواستهای PUT باید خوب باشند، زیرا بنا به تعریف فاقد قدرت هستند، به این معنی که مهم نیست یک عمل چند بار راهاندازی میشود، نتیجه چند بار فراخوانی آن با فراخوانی یک بار یکسان است.
ارسال درخواست POST به یک سرور لزوماً بی قدرت نیست، تماس چندباره آن ممکن است باعث یک رفتار ناخواسته یا ایجاد اشکال شود. اگر useEffect می تواند چندین بار راه اندازی شود چگونه می توانیم آن را مدیریت کنیم؟
پاسخ این است، سعی کنید از استفاده از useEffect به هیچ وجه اجتناب کنید. راه های مختلفی برای انجام این کار وجود دارد، اما بستگی به شرایط دارد. اگر تابع به وضعیت مؤلفه React وابسته نباشد، میتوان تابع را از مؤلفه خارج کرد.
مثال دیگر زمانی است که باعث ایجاد اثرات در تعاملات کاربر می شود. در این صورت اصلا نیازی به افکت ندارید. من اغلب کدی مشابه کد زیر می بینم.
import { useEffect } from 'react'
import { sendPostRequest } from '/services'
const Component = () => {
const [buttonClicked, setButtonClicked] = useState(false)
// Send a request when the button has been clicked.
useEffect(() => {
if (buttonClicked) {
sendPostRequest()
}
}, [buttonClicked])
return <button onClick={() => setButtonClicked(true))}>Click me</button>
}
مسئله این است که شما به آن useEffect نیاز ندارید، حتی useState. کد بالا باید شبیه کد زیر باشد.
import { useEffect } from 'react'
import { sendPostRequest } from '/services'
const Component = () => {
// This is what you should do if you really want to send the
// request when the button is clicked.
return <button onClick={() => sendPostRequest())}>Click me</button>
}
راه های زیادی برای خلاص شدن از useEffects وجود دارد، می توانید در گوگل جستجو کنید که چگونه از useEffect جلوگیری کنید. اما گاهی اوقات شما به افکت نیاز دارید، و در این صورت، باید مطمئن شوید که کد داخل آن فاقد قدرت است، و سپس اجازه دهید افکت هر طور که میخواهد اجرا شود.
TL; DR
یادگیری و استفاده از React روز به روز سخت تر می شود. بهینهسازیها به توسعهدهندگان واگذار میشود و نوشتن کد React به روشی نادرست آسانتر و آسانتر میشود. React خیلی زود دارای یک منحنی یادگیری شیب دار است و useEffect یکی از دلایل اصلی آن است.
برای استفاده بهینه از موقعیت، باید سعی کنید زیاد به بهینه سازی useEffects اهمیت ندهید – اجازه دهید useEffect بارها اجرا شود. سپس مطمئن شوید که کد داخل افکت فاقد قدرت است. برای واکشی داده ها، قلاب های بسیار خوبی مانند useSWR، useQuery و RTK Query دارید که به شما کمک می کند درخواست ها را در حافظه پنهان نگه دارید.
اگر حافظه نهان راه حل نیست، ممکن است یک انحراف یا دریچه گاز راه حل باشد. بسیاری از اوقات حتی میتوانید useEffect را به طور کامل حذف کنید، زیرا میتوان بدون آن کد را به روشی بهتر بازنویسی کرد.
یک روز ممکن است یک React بدون useEffect و شاید حتی بدون useCallback ببینیم؟ یا شاید مشکلات مربوط به قلاب useEffect همچنان در حال رشد باشد؟ در پایان، می توانم بگویم که React یک قلاب با یک چارچوب فوق العاده فاصله دارد.
