کامپوننت عملکردی React را صدا نکنید
«رفع «خطای پاسخ: قلابهای کمتر از حد انتظار ارائه شده»» را در egghead.io تماشا کنید
یک سوال عالی از Taranveer Bains دریافت کردم
در AMA من می پرسم:
من با مشکلی مواجه شدم که اگر تابعی را ارائه کنم که از قلاب در آن استفاده می کند
پیاده سازی و مقداری JSX را به callback برای برگرداند
Array.prototype.map. خطایی که گرفتم این بود
React Error: Rendered fewer hooks than expected.
در اینجا یک بازتولید ساده از آن خطا است
import * as React from 'react'
function Counter() {
const [count, setCount] = React.useState(0)
const increment = () => setCount((c) => c + 1)
return button onClick={increment}>{count}button>
}
function App() {
const [items, setItems] = React.useState([])
const addItem = () => setItems((i) => [...i, { id: i.length }])
return (
div>
button onClick={addItem}>Add Itembutton>
div>{items.map(Counter)}div>
div>
)
}
و در اینجا نحوه رفتار این هنگام ترسیم است (با حاشیه خطا در اطراف آن، بنابراین
ما این صفحه را خراب نمی کنیم):
کنسول جزئیات بیشتری را در پیامی مانند زیر آورده است:
Warning: React has detected a change in the order of Hooks
called by BadCounterList. This will lead to bugs and
errors if not fixed. For more information, read the
Rules of Hooks: https://fb.me/rules-of-hooks
Previous render Next render
------------------------------------------------------
1. useState useState
2. undefined useState
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
پس اینجا چه خبر است؟ بیایید حفاری کنیم.
ابتدا راه حل را به شما می گویم:
div>{items.map(Counter)}div>
div>{items.map(i => Counter key={i.id} />)}div>
قبل از اینکه شروع کنید به این فکر کنید که این مربوط به key پشتیبانی، اجازه دهید من فقط بگویم
شما این کار را نمی کنید اما به طور کلی تکیه گاه مهم است و می توانید یاد بگیرید
در مورد آن از پست وبلاگ دیگر من: درک React Key
پشتیبانی کنید
در اینجا روش دیگری برای انجام همان نوع اشتباه وجود دارد:
function Example() {
const [count, setCount] = React.useState(0)
let otherState
if (count > 0) {
React.useEffect(() => {
console.log('count', count)
})
}
const increment = () => setCount((c) => c + 1)
return button onClick={increment}>{count}button>
}
نکته این است که مال ماست Example جزء یک قلاب را به صورت مشروط فرا می خواند، این
مغایر با قوانین قلاب و
علت است
eslint-plugin-react-hooks
بسته دارای یک rules-of-hooks حکومت کند. می توانید در مورد این محدودیت بیشتر بخوانید
از اسناد React،
اما کافی است بگوییم که باید مطمئن شوید که قلاب ها همیشه صدا زده می شوند
همان تعداد دفعات برای یک جزء معین.
خوب، اما در مثال اول، ما قلاب ها را به صورت مشروط صدا نمی زنیم، درست است؟ و همینطور
چرا در این مورد برای ما مشکل ایجاد می کند؟
خوب، بیایید مثال اصلی خود را کمی بازنویسی کنیم:
function Counter() {
const [count, setCount] = React.useState(0)
const increment = () => setCount((c) => c + 1)
return button onClick={increment}>{count}button>
}
function App() {
const [items, setItems] = React.useState([])
const addItem = () => setItems((i) => [...i, { id: i.length }])
return (
div>
button onClick={addItem}>Add Itembutton>
div>
{items.map(() => {
return Counter()
})}
div>
div>
)
}
و متوجه خواهید شد که ما در حال ساخت تابعی هستیم که فقط تابع دیگری را فراخوانی می کند
تابع، پس بیایید این را درج کنیم:
function App() {
const [items, setItems] = React.useState([])
const addItem = () => setItems((i) => [...i, { id: i.length }])
return (
div>
button onClick={addItem}>Add Itembutton>
div>
{items.map(() => {
const [count, setCount] = React.useState(0)
const increment = () => setCount((c) => c + 1)
return button onClick={increment}>{count}button>
})}
div>
div>
)
}
شروع به ظاهر مشکل؟ متوجه خواهید شد که ما واقعاً چیزی را تغییر نداده ایم
رفتار این فقط یک بازساز است. اما آیا اکنون متوجه مشکل شده اید؟ اجازه بده
آنچه را که قبلاً گفتم تکرار کنید: باید مطمئن شوید که قلاب ها همیشه هستند
به همان تعداد بار تماس گرفت برای یک جزء داده شده
بر اساس refactor ما، ما متوجه شدیم که “مولفه داده شده” برای همه
مال ما useState تماس نمی گیرد App و Counter، اما در App به تنهایی این
به دلیل روشی است که ما با آن تماس می گیریم Counter جزء عملکردی این یک نیستنه یک جزء، بلکه یک تابع. React تفاوت بین فراخوانی یک تابع در JSX ما و داخل کردن آن را نمی داند. بنابراین نمی تواند چیزی را مرتبط کند
به Counter تابع زیرا به عنوان یک جزء ارائه نشده است.
به همین دلیل است که باید از JSX (یا React.createElement)
هنگام رندر کردن مؤلفه ها، نه فقط هنگام فراخوانی تابع. به این ترتیب هر کدام
قلاب هایی که استفاده می شوند را می توان با نمونه کامپوننت که ثبت کرد
React ایجاد می کند.
بنابراین اجزای تابع را فراخوانی نکنید. آنها را تصویر کنید.
اوه، و ذکر این نکته قابل توجه است گاهی اوقات برای فراخوانی یک تابع “کار می کند”.
اجزاء مانند این:
function Counter() {
const [count, setCount] = React.useState(0)
const increment = () => setCount((c) => c + 1)
return button onClick={increment}>{count}button>
}
function App() {
return (
div>
div>Here is a counter:div>
{Counter()}
div>
)
}
اما قلاب هایی که داخل هستند Counter مرتبط خواهد بود App جزء
به عنوان مثال، زیرا وجود ندارد Counter نمونه جزء بنابراین “کار خواهد کرد”،
اما نه آن طور که شما انتظار دارید، و می تواند در حین انجام شما به شیوه های غیرمنتظره ای رفتار کند
تغییر می کند. بنابراین فقط آن را به صورت عادی رندر کنید.
موفق باشید!
شما می توانید با این در codesandbox بازی کنید:




