diff --git a/book.html b/book.html index d72bede4..cc2667da 100644 --- a/book.html +++ b/book.html @@ -3586,7 +3586,7 @@
می تونیم به رخداد resize
توی componentDidMount
گوش کنیم و ابعاد(width
و height
) رو تغییر بدیم. البته باید حواسمون باشه که این listener رو باید توی متد componentWillUnmount
حذفش کنیم.
میتونیم به رخداد resize
توی componentDidMount
گوش کنیم و ابعاد(width
و height
) رو تغییر بدیم. البته باید حواسمون باشه که این listener رو باید توی متد componentWillUnmount
حذفش کنیم.
class WindowDimensions extends React.Component { constructor(props) { @@ -3650,12 +3650,12 @@تفاوت متدهای setState و replaceState چیه؟
-وقتی که از متد
+setState
فعلی و قبلی با هم ترکیب میشدند.replaceState
حالت فعلی رو نشون میده و با stateای میخواییم جایگزینش میکنه. معمولاsetState
برای این استفاده میشه که بنا به دلیلی بخواییم همه کلیدهای قبلی رو پاک کنیم. البته میشه بجای استفاده ازreplaceState
با استفاده ازsetState
بیاییم و state رو برابر باfalse
یاnull
قرار بدیم.وقتی که از متد
setState
روی کلاس کامپوننت استفاده میکنیم، مقادیر فعلی و قبلی با هم ترکیب میشن.replaceState
حالت فعلی رو با stateای که میخواییم جایگزینش میکنه. معمولا اگه ازsetState
برای جایگزین کردن استفاده کنیم، همه کلیدهای قبلی رو پاک کنیم. البته میشه بجای استفاده ازreplaceState
با استفاده ازsetState
بیاییم و state رو برابر باfalse
یاnull
قرار بدیم.
متدی که معرفی میشه در کلاس کامپوننتها هنگام به روز شدن state فراخوانی میشه. با استفاده از این متد میشه state و prop فعلی رو با مقادیر جدید مقایسه کرده و یه سری کار که مدنظر داریم رو انجام بدیم.
+توی کلاس کامپوننتها هنگام به روز شدن state یه سری متدها فراخوانی میشه. با استفاده از این متدها میشه state و prop فعلی رو با مقادیر جدید مقایسه کرده و یه سری کار که مدنظر داریم رو انجام بدیم.
componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)
@@ -3672,7 +3672,7 @@ روش توصیه شده برای حذف یک عنصر از آرایه توی state چیه؟
استفاده از متد Array.prototype.filter
آرایهها روش خوبیه.
-برای مثال بیایین یه تابع به اسم removeItem
برای به روز کردن state در نظر بگیریم.
+برای مثال میتونیم یه تابع به اسم removeItem
برای به روز کردن state به شکل زیر در نظر بگیریم.
removeItem(index) {
this.setState({
@@ -3715,7 +3715,7 @@ چطوری میشه با ریاکت یه JSON به شکل beautify شده نشون داد؟
-میشه با استفاده از تگ <pre>
و استفاده از optionهای متد JSON.stringify
این کار رو انجام داد:
+میشه گفت زیاد ربطی به ریاکت یا غیر ریاکت بودن برنامه نداره ولی در کل میشه با استفاده از تگ <pre>
و استفاده از optionهای متد JSON.stringify
این کار رو انجام داد:
const data = { name: "John", age: 42 };
@@ -3731,13 +3731,29 @@ چرا نمیتونیم prop رو آپدیت کنیم؟
-فلسفه ساختاری ریاکت طوریه که propها باید immutable باشن و بالا به پایین و به صورت سلسهمراتبی مقدار بگیرند. به این معنی که پدر هر کامپوننت میتونه هر مقداری رو به فرزند پاس بده و فرزند حق دستکاری اونو نداره.
+فلسفه ساختاری ریاکت به شکلیه که propها باید immutable باشن و از بالا به پایین و به صورت سلسهمراتبی مقدار بگیرند. به این معنی که پدر هر کامپوننت میتونه هر مقداری رو به فرزند پاس بده و فرزند حق دستکاری اونو نداره.
میشه با ایجاد یه ref برای المنت input
و استفاده از اون توی componentDidMount
یا useEffect
اینکار رو کرد:
const App = () => { + const nameInputRef = useRef(); + useEffect(() => { + nameInputRef.current.focus(); + }, []); + + return ( + <div> + <input defaultValue={"Won't focus"} /> + <input ref={nameInputRef} defaultValue={"Will focus"} /> + </div> + ); +}; ++
همین کد در کلاس کامپوننت:
+class App extends React.Component { componentDidMount() { this.nameInput.focus(); @@ -3758,45 +3774,29 @@.render(<App />, document.getElementById("app"));ReactDOM
const App = () => { - const nameInputRef = useRef(); - useEffect(() => { - nameInputRef.current.focus(); - }, []); - - return ( - <div> - <input defaultValue={"Won't focus"} /> - <input ref={nameInputRef} defaultValue={"Will focus"} /> - </div> - ); -}; -
فراخوانی متد setState
با استفاده از یه object برای ترکیب شدن اون:
فراخوانی متد setState
با استفاده از یه object برای ترکیب شدن اون:
استفاده از Object.assign
برای ایجاد یه کپی از object:
const user = Object.assign({}, this.state.user, { age: 42 }); this.setState({ user });-
* استفاده از عملگر *spread*: -+
استفاده از عملگر spread:
const user = {...this.state.user, age: 42 }; this.setState({ user });
فراخوانی setState
با یه تابع callback:
فراخوانی setState
با یه تابع callback: به این شکل میشه پیادهسازی کرد:
this.setState((prevState) => ({ @@ -3810,7 +3810,7 @@چرا توابع به جای object در setState ترجیح داده میشوند؟
-ریاکت اجازه ترکیب کردن تغییرات state رو با استفاده از متد
+setState
فراهم کرده است که باهث بهبود پرفورمنس میشه. چونthis.props
وthis.state
ممکنه به صورت asynchronous و همزمان به روز بشن، نباید به مقدار اونا برای محاسبه مقدار بعدی اعتماد کرد.ریاکت اجازه ترکیب کردن تغییرات state رو با استفاده از متد
setState
فراهم کرده، همین موضوع باعث بهبود پرفورمنس میشه. توی کلاس کامپوننتهاthis.props
وthis.state
ممکنه به صورت asynchronous و همزمان به روز بشن، نباید به مقدار اونا برای محاسبه مقدار بعدی اعتماد کرد.برای مثال به این شمارنده که درست کار نمیکنه دقت کنیم:
// Wrong @@ -3862,18 +3862,18 @@-
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Array.prototype.includes"></script>توی تکه کد فوق ما برای polyfill کردن
+Array.prototype.includes
درخواست دادیم.مثلا توی تکه کد فوق ما برای polyfill کردن
Array.prototype.includes
درخواست دادیم.
لازمه که کانفیگ HTTPS=true
رو برای env جاریستکنیم. میشه فایل package.json
بخش scripts رو به شکل پایین تغییر داد:
برای اینکار لازمه که کانفیگ HTTPS=true
رو برای env جاری ست کنیم، برای اینکار حتی لازم هم نیست فایل .env بسازیم و میتونیم توی فایل package.json
بخش scripts رو به شکل پایین تغییر بدیم:
"scripts": { "start": "set HTTPS=true && react-scripts start" }-
یا حتی set HTTPS=true && npm start
یا حتی به شکل set HTTPS=true && npm start
هم میشه تغییر داد.
NODE_PATH=src/app
بعد از این تغییر سرور develop رو ریستارت میکنیم بعدش دیگه میتونیم هر چیزی رو از مسیر src/app
بارگذاری کنیم و لازم هم نباشه مسیر کاملشو بهش بدیم.
بعد از این تغییر سرور develop رو ریستارت میکنیم بعدش دیگه میتونیم هر چیزی رو از مسیر src/app
بارگذاری کنیم و لازم هم نباشه مسیر کاملشو بهش بدیم. اینکار رو میشه با بخش module resolve توی webpack هم انجام داد.
یه listener به object history
اضافه میکنیم تا بتونیم لود شدن صفحه رو track کنیم:
یه listener به آبجکت history
اضافه میکنیم تا بتونیم لود شدن صفحه رو track کنیم:
history.listen(function (location) { window.ga("set", "page", location.pathname + location.search); window.ga("send", "pageview", location.pathname + location.search); });+
توی نسخه ۶ از react-router-dom دسترسی مستقیم به history برداشته شده تا پشتیبانی از suspense راحتتر باشه، توی این نسخه میشه از useLocation به شکل زیر استفاده کرد:
+ +const location = useLocation(); + +useEffect(() => { + window.ga("set", "page", location); + window.ga("send", "pageview", location); +}, [location]); +
لازمه که از setInterval
استفاده کنیم تا تغییرات رو اعمال کنیم و البته حواسمون هست که موقع unmount این interval رو حذف کنیم که memory leak نشه.
لازمه که از setInterval
استفاده کنیم تا تغییرات رو اعمال کنیم و البته حواسمون هست که موقع unmount این interval رو حذف کنیم که باعث memory leak نشه.
const intervalRef = useRef(); + +useEffect(() => { + intervalRef.current = setInterval(() => this.setState({ time: Date.now() }), 1000); + + return () => { + clearInterval(intervalRef.current); + } +}, [location]); ++
توی کلاس کامپوننت هم به شکل:
componentDidMount() { this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000) @@ -3908,19 +3929,11 @@(this.interval) } - -clearInterval
let interval; -useEffect(() { - interval = setInterval(() => this.setState({ time: Date.now() }), 1000); - - return () => clearInterval(interval); -}, []); -
ریاکت به شکل اتوماتیک پیشوندهای مخصوص مرورگرها رو اعمال نمیکنه. لازمه که تغییرات رو به شکل دستی اضافه کنیم.
+ریاکت به شکل اتوماتیک پیشوندهای مخصوص مرورگرها روی css رو اعمال نمیکنه. لازمه که تغییرات رو به شکل دستی اضافه کنیم.
<div
style={{
@@ -3945,15 +3958,14 @@ }
}
-با استفاده از شناساگر export کامپوننت MyProfile قراره یه عضو از ماژول فعلی میشه و برای import کردن لزومی به استفاده از عنوان این کامپوننت نیست.
+با استفاده از کلمه کلیدی export default میتونیم کامپوننت MyProfile(یا هر متغیر و کلاس دیگهای) رو به عنوان یه عضو از ماژول فعلی معرفی کرد و بعد از این، برای import کردن اون لزومی به استفاده از عنوان این کامپوننت نیست.
همه کامپوننتهای ریاکت لازم هست که با حرف بزرگ شروع بشن ولی در این مورد نیز یکسری استثناها وجود داره. تگهایی که با property و عملگر dot کار میکنن به عنوان کامپوننتهای با حرف کوچک تلقی میشن.
-For example the below tag can be compiled to a valid component,
همه کامپوننتهای ریاکت لازمه که با حرف بزرگ شروع بشن، ولی توی این مورد هم یه سری استثناها وجود داره. تگهایی که با property و عملگر dot کار میکنن رو میشه به عنوان کامپوننتهایی با حرف کوچک تلقی کرد. برای مثال این تگ میتونه syntax معتبری برای ریاکت باشه که با حروف کوچیک شروع میشه:
-render(){ +const Component = () => { return ( <obj.component /> // `React.createElement(obj.component)` ) @@ -3963,7 +3975,7 @@چرا تابع سازنده کلاس کامپوننت یکبار صدا زده میشه؟
-الگوریتم reconciliation ریاکت بعد از رندر کردن کامپوننت با بررسی رندرهای مجدد، بررسی میکنه که این کامپوننت قبلا رندر شده یا نه و اگه قبلا رندر شده باشه بر روی همون instance قبلی رندر رو انجام میده و instance جدیدی ساخته نمیشه پس تابع سازنده هم تنها یکبار صدا زده میشه.
+الگوریتم reconciliation ریاکت بعد از رندر کردن کامپوننت با بررسی رندرهای مجدد، بررسی میکنه که این کامپوننت قبلا رندر شده یا نه و اگه قبلا رندر شده باشه، تغییرات جدید رو روی همون instance قبلی رندر میکنه و instance جدیدی ساخته نمیشه، پس تابع سازنده هم تنها یکبار صدا زده میشه.
فیلدهای استاتیک بخشی از فیلدهای کلاس توی پروپوزال stage 3 هستن.
+فیلدهای استاتیک بخشی از فیلدهای کلاس(class properties) هستن که توی پروپوزال stage 3 معرفی شدن.
<input ref={(input) => (this.inputElement = input)} /> +<input ref={inputRef} />
-
اعمال رخداد click توی event handler:
this.inputElement.click(); +inputRef.click();
گروهبندی بر اساس ماهیت فایل:
-یک سبک مشهور دیگر گروهبندی فایلها براساس ماهیت اونهاست
+یک سبک مشهور دیگر گروهبندی فایلها براساس ماهیت اونهاست که حالا همین روش هم میتونه به شکلهای مختلف اجرا بشه ولی ساختار پایین میتونه یه مثال برای این روش باشه:
React Transition Group، React Spring و React Motion پکیجهای مشهور برای انیمیشن برای ریاکت هستن.
+React Transition Group، React Spring و React Motion پکیجهای مشهور برای انیمیشن برای ریاکت هستن.
ESLint یه linter برای JavaScript هستش۰. یه سری کتابخونه برای کمک به کدنویسی تو سبکهای مشخص و استاندارد برای eslint وجود داره. یکی از معروفترین پلاگینهای موجود eslint-plugin-react
هست.
-به صورت پیشفرض این پلاگین یه سری از best practiceها رو برای کدهای نوشته شده بررسی میکنه. با مجموعهای از قوانین برای. پلاگین مشهور دیگه eslint-plugin-jsx-a11y
هستش، که برای مسائل معروف در زمینه accessibility کمکمیکنه. چرا که JSX یه سینتکس متفاوتتری از HTML ارائه میکنه، مشکلاتی که ممکنه مثلا با alt
و tabindex
پیش میاد رو با این پلاگین میشه متوجه شد.
ESLint یه linter برای JavaScript هستش. یه سری کتابخونه برای کمک به کدنویسی تو سبکهای مشخص و استاندارد برای eslint وجود داره. یکی از معروفترین پلاگینهای موجود eslint-plugin-react
هست.
+به صورت پیشفرض این پلاگین یه سری از best practiceها رو برای کدهای نوشته شده بررسی میکنه و یه مجموعه از قوانین رو برای کدنویسی الزام میکنه. پلاگین مشهور دیگه eslint-plugin-jsx-a11y
هستش، که برای بررسی نکات و ملزومات معروف در زمینه accessibility کمک میکنه. چرا که JSX یه سینتکس متفاوتتری از HTML ارائه میکنه، مشکلاتی که ممکنه مثلا با alt
و tabindex
پیش میاد رو با این پلاگین میشه متوجه شد.
const MyComponent = () => { + const [employees, setEmployees] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + fetch("https://api.example.com/items") + .then((res) => res.json()) + .then( + (result) => { + setEmployees(result.employees); + }, + (error) => { + setError(error); + } + ); + }, []); + + return error ? ( + <div>Error: {error.message}</div> + ): ( + <ul> + {employees.map((employee) => ( + <li key={employee.name}> + {employee.name}-{employee.experience} + </li> + ))} + </ul> + ); +}; +
همین کد روی کلاس کامپوننت به شکل زیر اجرا میشد:
+class MyComponent extends React.Component { constructor(props) { super(props); @@ -4131,42 +4175,11 @@} - -}
const MyComponent = () => { - const [employees, setEmployees] = useState([]); - const [error, setError] = useState(null); - - useEffect(() => { - fetch("https://api.example.com/items") - .then((res) => res.json()) - .then( - (result) => { - setEmployees(result.employees); - }, - (error) => { - setError(error); - } - ); - }, []); - - return error ? ( - <div>Error: {error.message}</div> - ): ( - <ul> - {employees.map((employee) => ( - <li key={employee.name}> - {employee.name}-{employee.experience} - </li> - ))} - </ul> - ); -}; -
Render Props یه تکنیک ساده برای به اشتراک گذاری کد بین کامپوننتهاست که با استفاده از یه prop که یه تابع رو بهش دادیم انجام میشه. کامپوننت زیر از همین روش برای پاس دادن یه React element استفاده میکنه.
+Render Props یه تکنیک ساده برای به اشتراک گذاری کامپوننت بین کامپوننتهای دیگهـست که با استفاده از یه prop که یه تابع یا یه کامپوننت رو بهش دادیم انجام میشه. کامپوننت زیر از همین روش برای پاس دادن یه React element استفاده میکنه و توی کامپوننت پایین این prop رو یه شکل یه تابع فراخوانی میکنیم و چون یه تابع هست، میتونیم بهش هر مقداری که میخواییم بیاریم این سمت رو پاس بدیم.
<DataProvider render={(data) => <h1>{`Hello ${data.target}`}</h1>} />
@@ -4183,7 +4196,7 @@ React Router یک wrapper روی کتابخونه history
هستش که اعمال اجرایی بر روی window.history
رو با استفاده از ابجکتهای hash و browser مدیریت میکنه. البته این کتابخونه یک نوع دیگه از historyها به اسم memory history رو هم معرفی میکنه که برای محیطهایی که به صورت عمومی از history پشتیبانی نمیکنن کاربرد داره. مثل محیط توسعه برنامه موبایل با (React Native) یا محیطهای unit test و Nodejs.
React Router یه wrapper روی کتابخونه history
هست که اعمال اجرایی بر روی window.history
رو با استفاده از ابجکتهای hash و browser مدیریت میکنه. البته این کتابخونه یک نوع دیگه از historyها به اسم memory history رو هم معرفی میکنه که برای محیطهایی که به صورت عمومی از history پشتیبانی نمیکنن کاربرد داره. مثل محیط توسعه برنامه موبایل با (React Native) یا محیطهای unit test و Nodejs.
هر شئ از history دو متد برای جابجایی ارائه میدهد.
+هر شئ از آبجکت history دو تا متد برای کار با state مرورگر ارائه میده.
push
replace
اگر به history به عنوان یک آرایه از مسیرهای بازدید شده نگاه کنیم، push
یک جابجایی جدید به مسیر اضافه میکنه و replace
مسیر فعلی را با یک مسیر جدید جابجا میکنه.
اگه به history به شکل یک آرایه از مسیرهای بازدید شده نگاه کنیم، push
یک جابجایی جدید به مسیر اضافه میکنه و replace
مسیر فعلی رو با یه مسیر جدید جایگزین میکنه.
روشهای مختلفی برای جابجایی در برنامه و توسط کد وجود دارد.
+روشهای مختلفی برای جابجایی در برنامه و توسط کد وجود داره که پایین لیست میکنیم، ولی روش آخر(استفاده از هوکها) بهترین و سادهترین روش توی کامپوننتهای تابعی هست.
استفاده از تابع مرتبه بالاتر(higher-order) withRouter
:
متد withRouter
آبجکت history را به عنوان یک prop به کامپوننت اضافه میکنه. در این prop دسترسی به متدهای push
و replace
بسادگی میتونه مسیریابی بین کامپوننت رو فراهم کنه و نیاز به context رو رفع کنه.
متد withRouter
آبجکت history رو به عنوان یه prop به کامپوننت اضافه میکنه. روی این prop به متدهای push
و replace
دسترسی داریم که بهسادگی میتونه مسیریابی بین routeها رو فراهم کنه و نیاز به context رو رفع کنه.
استفاده از هوکهای موجود:
-هوکهایی برای دسترسی به history و params در این کتابخونه وجود داره مثل useHistory:
+هوکهایی برای دسترسی به history و params در این کتابخونه وجود داره مثل useHistory یا حتی توی نسخه ۶ به بعد هوک useNavigate که راحتتر میتونه امکان navigate بین صفحات رو فراهم کنه:
نسخه ۶:
+ +const Page = (props, context) => { + const navigate = useNavigate(); + + return ( + <button + type="button" + onClick={() => { + navigate("/new-location"); + }} + > + {"Click Me!"} + </button> + ); +}; +
باید کامپوننت Route رو توی بلاک <Switch>
قرار بدیم چون <Switch>
چون Switch باعث میشه که منحصرا یک کامپوننت در صفحه لود بشه.
اولش لازمه که Switch
رو import کنیم:
باید کامپوننت Route رو توی بلاک <Switch>
قرار بدیم چون همین کامپوننت <Switch>
چون Switch هست که باعث میشه منحصرا فقط یه route با مسیر فعلی تطابق پیدا کنه و کامپوننت اون route توی صفحه رندر بشه. اولش لازمه که Switch
رو import کنیم:
import { Switch, Router, Route } from "react-router";-
بعدش رووتها رو توی بلاک <Switch>
تعریف میکنیم:
بعدش routeها رو <Switch>
تعریف میکنیم:
<Router> <Switch> @@ -4334,7 +4363,7 @@چطوری میشه به متد history.push پارامتر اضافه کرد؟
-موقع جابجایی میتونیم یه object به
+history
پاس بدیم که یه سری گزینهها رو برامون قابل کانفیگ میکنه:همونطوری که میدونیم موقع جابجایی میشه یه object به
history
پاس بدیم که یه سری گزینهها رو برامون قابل کانفیگ میکنه:this.props.history.push({ pathname: "/template", @@ -4347,7 +4376,7 @@+چطوری میشه صفحه ۴۰۴ ساخت؟
-کامپوننت
+<Switch>
اولین فرزند<Route>
ای که با درخواست موجود تطابق داشته باشه رو رندر میکنه. از اونجایی که یه<Route>
بدون path یا با path * همیشه مطابق با درخواست است، پس هنگام خطای ۴۰۴ این مورد برای رندر استفاده میشه.کامپوننت
<Switch>
اولین فرزند<Route>
ای که با درخواست موجود تطابق داشته باشه رو رندر میکنه. از اونجایی که یه<Route>
بدون path یا با path * همیشه مطابق با درخواستهاست، پس هنگام خطای ۴۰۴ این مورد برای رندر استفاده میشه.<Switch> <Route exact path="/" component={Home} /> @@ -4396,22 +4425,21 @@push("/go-here");.
نکته: روی نسخه ۶ دسترسی مستقیم به history حذف شده و برای هر کار یه هوک مختص به اون کار مهیا شده.
پکیج react-router
مکان استفاده از کامپوننت <Redirect>
رو توی React Router میده. رندر کردن <Redirect>
باعث جابجایی به مسیر پاس داده شده بهش میشه. مثل ریدایرکت سرور-ساید، مسیر جدید با path فعلی جایگزین میشه.
پکیج react-router
امکان استفاده از کامپوننت <Redirect>
رو توی React Router فراهم میکنه. رندر کردن <Redirect>
باعث جابجایی به مسیر پاس داده شده میشه. دقیقا مثل ریدایرکت سمت سرور، path مسیر جدید با path فعلی جایگزین میشه.
import React, { Component } from "react"; import { Redirect } from "react-router"; -export default class LoginComponent extends Component { - render() { - if (this.state.isLoggedIn === true) { - return <Redirect to="/your/redirect/page" />; - } else { - return <div>{"Login Please"}</div>; - } +const Component = () => { + if (isLoggedIn === true) { + return <Redirect to="/your/redirect/page" />; + } else { + return <div>{"Login Please"}</div>; } }@@ -4422,7 +4450,7 @@
React Intl یه کتابخونه برای راحت کردن کار با برنامههای چند زبانهست. این کتابخونه از مجموعهای از کامپوننتها و APIها برای فرمتبندی string، date و اعداد برای سهولت چندزبانگی استفاده میکنه. React Intl بخشی از FormatJS هست که امکان اتصال به ریاکت رو با کامپوننتهای خودش فراهم میکنه.
+React Intl یه کتابخونه برای آسان نمودن توسعه برنامههای چند زبانهـست. این کتابخونه از مجموعهای از کامپوننتها و APIها برای فرمتبندی رشتهها، تاریخ و اعداد رو برای سادهسازی فرآیند چندزبانگی فراهم میکنه. React Intl بخشی از FormatJS هست که امکان اتصال به ریاکت رو با کامپوننتهای خودش فراهم میکنه.
` و استفاده از optionهای متد `JSON.stringify` این کار رو انجام داد: + میشه گفت زیاد ربطی به ریاکت یا غیر ریاکت بودن برنامه نداره ولی در کل میشه با استفاده از تگ `` و استفاده از optionهای متد `JSON.stringify` این کار رو انجام داد: @@ -3108,7 +3108,7 @@ puppeteer: 106. ### چرا نمیتونیم prop رو آپدیت کنیم؟ - فلسفه ساختاری ریاکت طوریه که propها باید _immutable_ باشن و _بالا به پایین_ و به صورت سلسهمراتبی مقدار بگیرند. به این معنی که پدر هر کامپوننت میتونه هر مقداری رو به فرزند پاس بده و فرزند حق دستکاری اونو نداره. + فلسفه ساختاری ریاکت به شکلیه که propها باید _immutable_ باشن و از بالا به پایین و به صورت سلسهمراتبی مقدار بگیرند. به این معنی که پدر هر کامپوننت میتونه هر مقداری رو به فرزند پاس بده و فرزند حق دستکاری اونو نداره. **[فهرست](#فهرست)** @@ -3118,6 +3118,28 @@ puppeteer: + ```jsx harmony + const App = () => { + const nameInputRef = useRef(); + useEffect(() => { + nameInputRef.current.focus(); + }, []); + + return ( ++ + ++ ); + }; + ``` + + + + همین کد در کلاس کامپوننت: + + + ```jsx harmony class App extends React.Component { componentDidMount() { @@ -3142,33 +3164,13 @@ puppeteer: - - - ```jsx harmony - const App = () => { - const nameInputRef = useRef(); - useEffect(() => { - nameInputRef.current.focus(); - }, []); - - return ( -- - -- ); - }; - ``` - - - **[فهرست](#فهرست)** 108. ### روشهای ممکن برای آپدیت کردن object توی state کدوما هستن؟ 1. **فراخوانی متد `setState` با استفاده از یه object برای ترکیب شدن اون:** - - - استفاده از `Object.assign` برای ایجاد یه کپی از object: + + - استفاده از `Object.assign` برای ایجاد یه کپی از object: @@ -3179,7 +3181,7 @@ puppeteer: - * استفاده از عملگر *spread*: + - استفاده از عملگر *spread*: @@ -3190,7 +3192,7 @@ puppeteer: - 2. **فراخوانی `setState` با یه تابع callback:** + 2. **فراخوانی `setState` با یه تابع callback:** به این شکل میشه پیادهسازی کرد: @@ -3209,7 +3211,7 @@ puppeteer: 109. ### چرا توابع به جای object در setState ترجیح داده میشوند؟ - ریاکت اجازه ترکیب کردن تغییرات state رو با استفاده از متد `setState` فراهم کرده است که باهث بهبود پرفورمنس میشه. چون `this.props` و `this.state` ممکنه به صورت asynchronous و همزمان به روز بشن، نباید به مقدار اونا برای محاسبه مقدار بعدی اعتماد کرد. + ریاکت اجازه ترکیب کردن تغییرات state رو با استفاده از متد `setState` فراهم کرده، همین موضوع باعث بهبود پرفورمنس میشه. توی کلاس کامپوننتها `this.props` و `this.state` ممکنه به صورت asynchronous و همزمان به روز بشن، نباید به مقدار اونا برای محاسبه مقدار بعدی اعتماد کرد. برای مثال به این شمارنده که درست کار نمیکنه دقت کنیم: @@ -3286,13 +3288,13 @@ puppeteer: - توی تکه کد فوق ما برای polyfill کردن `Array.prototype.includes` درخواست دادیم. + مثلا توی تکه کد فوق ما برای polyfill کردن `Array.prototype.includes` درخواست دادیم. **[فهرست](#فهرست)** 112. ### توی CRA چطوری از https بهجای http استفاده کنیم؟ - لازمه که کانفیگ `HTTPS=true` رو برای env جاریستکنیم. میشه فایل `package.json` بخش scripts رو به شکل پایین تغییر داد: + برای اینکار لازمه که کانفیگ `HTTPS=true` رو برای env جاری ست کنیم، برای اینکار حتی لازم هم نیست فایل .env بسازیم و میتونیم توی فایل `package.json` بخش scripts رو به شکل پایین تغییر بدیم: @@ -3304,7 +3306,7 @@ puppeteer: - یا حتی `set HTTPS=true && npm start` + یا حتی به شکل `set HTTPS=true && npm start` هم میشه تغییر داد. **[فهرست](#فهرست)** @@ -3320,13 +3322,13 @@ puppeteer: - بعد از این تغییر سرور develop رو ریستارت میکنیم بعدش دیگه میتونیم هر چیزی رو از مسیر `src/app` بارگذاری کنیم و لازم هم نباشه مسیر کاملشو بهش بدیم. + بعد از این تغییر سرور develop رو ریستارت میکنیم بعدش دیگه میتونیم هر چیزی رو از مسیر `src/app` بارگذاری کنیم و لازم هم نباشه مسیر کاملشو بهش بدیم. اینکار رو میشه با بخش module resolve توی webpack هم انجام داد. **[فهرست](#فهرست)** 114. ### چطوری میشه Google Analytics رو به react-router اضافه کرد؟ - یه listener به object `history` اضافه میکنیم تا بتونیم لود شدن صفحه رو track کنیم: + یه listener به آبجکت `history` اضافه میکنیم تا بتونیم لود شدن صفحه رو track کنیم: @@ -3339,34 +3341,55 @@ puppeteer: + توی نسخه ۶ از react-router-dom دسترسی مستقیم به history برداشته شده تا پشتیبانی از suspense راحتتر باشه، توی این نسخه میشه از useLocation به شکل زیر استفاده کرد: + + + + ```javascript + const location = useLocation(); + + useEffect(() => { + window.ga("set", "page", location); + window.ga("send", "pageview", location); + }, [location]); + ``` + + + **[فهرست](#فهرست)** 115. ### چطوری یه کامپوننت رو هر ثانیه به روز کنیم؟ - لازمه که از `setInterval` استفاده کنیم تا تغییرات رو اعمال کنیم و البته حواسمون هست که موقع unmount این interval رو حذف کنیم که memory leak نشه. + لازمه که از `setInterval` استفاده کنیم تا تغییرات رو اعمال کنیم و البته حواسمون هست که موقع unmount این interval رو حذف کنیم که باعث memory leak نشه. ```javascript - componentDidMount() { - this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000) - } - - componentWillUnmount() { - clearInterval(this.interval) - } + const intervalRef = useRef(); + + useEffect(() => { + intervalRef.current = setInterval(() => this.setState({ time: Date.now() }), 1000); + + return () => { + clearInterval(intervalRef.current); + } + }, [location]); ``` + + توی کلاس کامپوننت هم به شکل: + ```javascript - let interval; - useEffect(() { - interval = setInterval(() => this.setState({ time: Date.now() }), 1000); + componentDidMount() { + this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000) + } - return () => clearInterval(interval); - }, []); + componentWillUnmount() { + clearInterval(this.interval) + } ``` @@ -3375,7 +3398,7 @@ puppeteer: 116. ### برای استایلدهیهای درون خطی چطوری باید پیشوندهای مخصوص مرورگرها رو اضافه کرد؟ - ریاکت به شکل اتوماتیک *پیشوندهای مخصوص مرورگر*ها رو اعمال _نمیکنه_. لازمه که تغییرات رو به شکل دستی اضافه کنیم. + ریاکت به شکل اتوماتیک پیشوندهای مخصوص مرورگرها روی css رو اعمال **نمیکنه**. لازمه که تغییرات رو به شکل دستی اضافه کنیم. @@ -3412,19 +3435,18 @@ puppeteer: - با استفاده از شناساگر export کامپوننت MyProfile قراره یه عضو از ماژول فعلی میشه و برای import کردن لزومی به استفاده از عنوان این کامپوننت نیست. + با استفاده از کلمه کلیدی export default میتونیم کامپوننت MyProfile(یا هر متغیر و کلاس دیگهای) رو به عنوان یه عضو از ماژول فعلی معرفی کرد و بعد از این، برای import کردن اون لزومی به استفاده از عنوان این کامپوننت نیست. **[فهرست](#فهرست)** 118. ### استثنایی که برای نامگذاری کامپوننت اجازه استفاده از حرف کوچک رو میده چیه؟ - همه کامپوننتهای ریاکت لازم هست که با حرف بزرگ شروع بشن ولی در این مورد نیز یکسری استثناها وجود داره. تگهایی که با property و عملگر dot کار میکنن به عنوان کامپوننتهای با حرف کوچک تلقی میشن. - For example the below tag can be compiled to a valid component, + همه کامپوننتهای ریاکت لازمه که با حرف بزرگ شروع بشن، ولی توی این مورد هم یه سری استثناها وجود داره. تگهایی که با property و عملگر dot کار میکنن رو میشه به عنوان کامپوننتهایی با حرف کوچک تلقی کرد. برای مثال این تگ میتونه syntax معتبری برای ریاکت باشه که با حروف کوچیک شروع میشه: ```jsx - render(){ + const Component = () => { return (// `React.createElement(obj.component)` ) @@ -3437,7 +3459,7 @@ puppeteer: 119. ### چرا تابع سازنده کلاس کامپوننت یکبار صدا زده میشه؟ - الگوریتم _reconciliation_ ریاکت بعد از رندر کردن کامپوننت با بررسی رندرهای مجدد، بررسی میکنه که این کامپوننت قبلا رندر شده یا نه و اگه قبلا رندر شده باشه بر روی همون instance قبلی رندر رو انجام میده و instance جدیدی ساخته نمیشه پس تابع سازنده هم تنها یکبار صدا زده میشه. + الگوریتم reconciliation ریاکت بعد از رندر کردن کامپوننت با بررسی رندرهای مجدد، بررسی میکنه که این کامپوننت قبلا رندر شده یا نه و اگه قبلا رندر شده باشه، تغییرات جدید رو روی همون instance قبلی رندر میکنه و instance جدیدی ساخته نمیشه، پس تابع سازنده هم تنها یکبار صدا زده میشه. **[فهرست](#فهرست)** @@ -3455,7 +3477,7 @@ puppeteer: - _فیلدهای استاتیک_ بخشی از _فیلدهای کلاس_ توی پروپوزال stage 3 هستن. + فیلدهای استاتیک بخشی از فیلدهای کلاس(class properties) هستن که توی پروپوزال stage 3 معرفی شدن. **[فهرست](#فهرست)** @@ -3469,7 +3491,7 @@ puppeteer: ```jsx harmony - (this.inputElement = input)} /> + ``` @@ -3479,7 +3501,7 @@ puppeteer: ```javascript - this.inputElement.click(); + inputRef.click(); ``` @@ -3496,9 +3518,9 @@ puppeteer: دو روش معروف برای پوشههای ریاکت وجود داره: - 1. **گروه بندی براساس وپژگی یا route:** + 1. **گروه بندی براساس ویژگی یا route:** - یک روش معروف قراردادن فایلهای CSS، JS و تستها کنارهم به ازای هر ویژگی یا route هست + یک روش معروف قراردادن فایلهای CSS، JS و تستها کنارهم به ازای هر ویژگی یا route هست، مثل این ساختار: @@ -3527,7 +3549,7 @@ puppeteer: 2. **گروهبندی بر اساس ماهیت فایل:** - یک سبک مشهور دیگر گروهبندی فایلها براساس ماهیت اونهاست + یک سبک مشهور دیگر گروهبندی فایلها براساس ماهیت اونهاست که حالا همین روش هم میتونه به شکلهای مختلف اجرا بشه ولی ساختار پایین میتونه یه مثال برای این روش باشه: @@ -3555,7 +3577,7 @@ puppeteer: 124. ### پکیجهای مشهور برای انیمیشن کدوما هستن؟ - _React Transition Group_، _React Spring_ و _React Motion_ پکیجهای مشهور برای انیمیشن برای ریاکت هستن. + React Transition Group، React Spring و React Motion پکیجهای مشهور برای انیمیشن برای ریاکت هستن. **[فهرست](#فهرست)** @@ -3593,8 +3615,8 @@ puppeteer: 126. ### معروفترین linterهای ریاکت کدوما هستن؟ - ESLint یه linter برای JavaScript هستش۰. یه سری کتابخونه برای کمک به کدنویسی تو سبکهای مشخص و استاندارد برای eslint وجود داره. یکی از معروفترین پلاگینهای موجود `eslint-plugin-react` هست. - به صورت پیشفرض این پلاگین یه سری از best practiceها رو برای کدهای نوشته شده بررسی میکنه. با مجموعهای از قوانین برای. پلاگین مشهور دیگه `eslint-plugin-jsx-a11y` هستش، که برای مسائل معروف در زمینه accessibility کمکمیکنه. چرا که JSX یه سینتکس متفاوتتری از HTML ارائه میکنه، مشکلاتی که ممکنه مثلا با `alt` و `tabindex` پیش میاد رو با این پلاگین میشه متوجه شد. + ESLint یه linter برای JavaScript هستش. یه سری کتابخونه برای کمک به کدنویسی تو سبکهای مشخص و استاندارد برای eslint وجود داره. یکی از معروفترین پلاگینهای موجود `eslint-plugin-react` هست. + به صورت پیشفرض این پلاگین یه سری از best practiceها رو برای کدهای نوشته شده بررسی میکنه و یه مجموعه از قوانین رو برای کدنویسی الزام میکنه. پلاگین مشهور دیگه `eslint-plugin-jsx-a11y` هستش، که برای بررسی نکات و ملزومات معروف در زمینه accessibility کمک میکنه. چرا که JSX یه سینتکس متفاوتتری از HTML ارائه میکنه، مشکلاتی که ممکنه مثلا با `alt` و `tabindex` پیش میاد رو با این پلاگین میشه متوجه شد. **[فهرست](#فهرست)** @@ -3606,6 +3628,44 @@ puppeteer: + ```jsx harmony + const MyComponent = () => { + const [employees, setEmployees] = useState([]); + const [error, setError] = useState(null); + + useEffect(() => { + fetch("https://api.example.com/items") + .then((res) => res.json()) + .then( + (result) => { + setEmployees(result.employees); + }, + (error) => { + setError(error); + } + ); + }, []); + + return error ? ( + Error: {error.message}+ ): ( ++ {employees.map((employee) => ( +
+ ); + }; + ``` + + + + همین کد روی کلاس کامپوننت به شکل زیر اجرا میشد: + + + ```jsx harmony class MyComponent extends React.Component { constructor(props) { @@ -3652,47 +3712,11 @@ puppeteer: - - - ```jsx harmony - const MyComponent = () => { - const [employees, setEmployees] = useState([]); - const [error, setError] = useState(null); - - useEffect(() => { - fetch("https://api.example.com/items") - .then((res) => res.json()) - .then( - (result) => { - setEmployees(result.employees); - }, - (error) => { - setError(error); - } - ); - }, []); - - return error ? ( -- + {employee.name}-{employee.experience} +
+ ))} +Error: {error.message}- ): ( -- {employees.map((employee) => ( -
- ); - }; - ``` - - - **[فهرست](#فهرست)** 128. ### render props چیه؟ - **Render Props** یه تکنیک ساده برای به اشتراک گذاری کد بین کامپوننتهاست که با استفاده از یه prop که یه تابع رو بهش دادیم انجام میشه. کامپوننت زیر از همین روش برای پاس دادن یه React element استفاده میکنه. + **Render Props** یه تکنیک ساده برای به اشتراک گذاری کامپوننت بین کامپوننتهای دیگهـست که با استفاده از یه prop که یه تابع یا یه کامپوننت رو بهش دادیم انجام میشه. کامپوننت زیر از همین روش برای پاس دادن یه React element استفاده میکنه و توی کامپوننت پایین این prop رو یه شکل یه تابع فراخوانی میکنیم و چون یه تابع هست، میتونیم بهش هر مقداری که میخواییم بیاریم این سمت رو پاس بدیم. @@ -3714,7 +3738,7 @@ puppeteer: 130. ### ارتباط React Router و کتابخونه history چیه؟ - React Router یک wrapper روی کتابخونه `history` هستش که اعمال اجرایی بر روی `window.history` رو با استفاده از ابجکتهای hash و browser مدیریت میکنه. البته این کتابخونه یک نوع دیگه از historyها به اسم memory history رو هم معرفی میکنه که برای محیطهایی که به صورت عمومی از history پشتیبانی نمیکنن کاربرد داره. مثل محیط توسعه برنامه موبایل با (React Native) یا محیطهای unit test و Nodejs. + React Router یه wrapper روی کتابخونه `history` هست که اعمال اجرایی بر روی `window.history` رو با استفاده از ابجکتهای hash و browser مدیریت میکنه. البته این کتابخونه یک نوع دیگه از historyها به اسم memory history رو هم معرفی میکنه که برای محیطهایی که به صورت عمومی از history پشتیبانی نمیکنن کاربرد داره. مثل محیط توسعه برنامه موبایل با (React Native) یا محیطهای unit test و Nodejs. **[فهرست](#فهرست)** @@ -3726,28 +3750,27 @@ puppeteer: 2. `- - {employee.name}-{employee.experience} -
- ))} -` 3. ` ` - کامپوننتهای فوق به ترتیب _browser_، _hash_، و _memory_ history درست میکنن. React Router v4 ساخت `history` را براساس context ارائه شده به آبجکت router انجام میدهد. + کامپوننتهای فوق به ترتیب browser، hash، و memory history درست میکنن. React Router v4 ساخت `history` رو براساس context ارائه شده به آبجکت router انجام میده و همین موضوعه که باعث میشه بتونیم از این کتابخونه توی محیطهای مختلف استفاده کنیم. **[فهرست](#فهرست)** 132. ### هدف از متدهای push و replace توی history چیه؟ - هر شئ از history دو متد برای جابجایی ارائه میدهد. + هر شئ از آبجکت history دو تا متد برای کار با state مرورگر ارائه میده. 1. `push` 2. `replace` - اگر به history به عنوان یک آرایه از مسیرهای بازدید شده نگاه کنیم، `push` یک جابجایی جدید به مسیر اضافه میکنه و `replace` مسیر فعلی را با یک مسیر جدید جابجا میکنه. + اگه به history به شکل یک آرایه از مسیرهای بازدید شده نگاه کنیم، `push` یک جابجایی جدید به مسیر اضافه میکنه و `replace` مسیر فعلی رو با یه مسیر جدید جایگزین میکنه. **[فهرست](#فهرست)** 133. ### چطوری توی برنامه به route خاص جابجا بشیم؟ - - روشهای مختلفی برای جابجایی در برنامه و توسط کد وجود دارد. + روشهای مختلفی برای جابجایی در برنامه و توسط کد وجود داره که پایین لیست میکنیم، ولی روش آخر(استفاده از هوکها) بهترین و سادهترین روش توی کامپوننتهای تابعی هست. 1. **استفاده از تابع مرتبه بالاتر(higher-order) `withRouter`:** - متد `withRouter` آبجکت history را به عنوان یک prop به کامپوننت اضافه میکنه. در این prop دسترسی به متدهای `push` و `replace` بسادگی میتونه مسیریابی بین کامپوننت رو فراهم کنه و نیاز به context رو رفع کنه. + متد `withRouter` آبجکت history رو به عنوان یه prop به کامپوننت اضافه میکنه. روی این prop به متدهای `push` و `replace` دسترسی داریم که بهسادگی میتونه مسیریابی بین routeها رو فراهم کنه و نیاز به context رو رفع کنه. @@ -3824,7 +3847,7 @@ puppeteer: 4. **استفاده از هوکهای موجود:** - هوکهایی برای دسترسی به history و params در این کتابخونه وجود داره مثل useHistory: + هوکهایی برای دسترسی به history و params در این کتابخونه وجود داره مثل useHistory یا حتی توی نسخه ۶ به بعد هوک useNavigate که راحتتر میتونه امکان navigate بین صفحات رو فراهم کنه: @@ -3847,6 +3870,29 @@ puppeteer: }; ``` + + + نسخه ۶: + + + + ```jsx harmony + const Page = (props, context) => { + const navigate = useNavigate(); + + return ( + + ); + }; + ``` + **[فهرست](#فهرست)** @@ -3869,9 +3915,7 @@ puppeteer: 135. ### دلیل خطای "Router may have only one child element" چیه؟ - باید کامپوننت Route رو توی بلاک ` ` قرار بدیم چون ` ` چون Switch باعث میشه که منحصرا یک کامپوننت در صفحه لود بشه. - - اولش لازمه که `Switch` رو import کنیم: + باید کامپوننت Route رو توی بلاک ` ` قرار بدیم چون همین کامپوننت ` ` چون Switch هست که باعث میشه منحصرا فقط یه route با مسیر فعلی تطابق پیدا کنه و کامپوننت اون route توی صفحه رندر بشه. اولش لازمه که `Switch` رو import کنیم: @@ -3881,7 +3925,7 @@ puppeteer: - بعدش رووتها رو توی بلاک ` ` تعریف میکنیم: + بعدش routeها رو ` ` تعریف میکنیم: @@ -3900,7 +3944,7 @@ puppeteer: 136. ### چطوری میشه به متد history.push پارامتر اضافه کرد؟ - موقع جابجایی میتونیم یه object به `history` پاس بدیم که یه سری گزینهها رو برامون قابل کانفیگ میکنه: + همونطوری که میدونیم موقع جابجایی میشه یه object به `history` پاس بدیم که یه سری گزینهها رو برامون قابل کانفیگ میکنه: @@ -3920,7 +3964,7 @@ puppeteer: 137. ### چطوری میشه صفحه ۴۰۴ ساخت؟ - کامپوننت ` ` اولین فرزند ` `ای که با درخواست موجود تطابق داشته باشه رو رندر میکنه. از اونجایی که یه ` ` بدون path یا با path \* همیشه مطابق با درخواست است، پس هنگام خطای ۴۰۴ این مورد برای رندر استفاده میشه. + کامپوننت ` ` اولین فرزند ` `ای که با درخواست موجود تطابق داشته باشه رو رندر میکنه. از اونجایی که یه ` ` بدون path یا با path \* همیشه مطابق با درخواستهاست، پس هنگام خطای ۴۰۴ این مورد برای رندر استفاده میشه. @@ -3986,25 +4030,25 @@ puppeteer: + **نکته:** روی نسخه ۶ دسترسی مستقیم به history حذف شده و برای هر کار یه هوک مختص به اون کار مهیا شده. + **[فهرست](#فهرست)** 139. ### چطوری بعد از لاگین به شکل خودکار ریدایرکت کنیم؟ - پکیج `react-router` مکان استفاده از کامپوننت ` ` رو توی React Router میده. رندر کردن ` ` باعث جابجایی به مسیر پاس داده شده بهش میشه. مثل ریدایرکت سرور-ساید، مسیر جدید با path فعلی جایگزین میشه. + پکیج `react-router` امکان استفاده از کامپوننت ` ` رو توی React Router فراهم میکنه. رندر کردن ` ` باعث جابجایی به مسیر پاس داده شده میشه. دقیقا مثل ریدایرکت سمت سرور، path مسیر جدید با path فعلی جایگزین میشه. ```javascript import React, { Component } from "react"; import { Redirect } from "react-router"; - - export default class LoginComponent extends Component { - render() { - if (this.state.isLoggedIn === true) { - return ; - } else { - return {"Login Please"}; - } + + const Component = () => { + if (isLoggedIn === true) { + return; + } else { + return {"Login Please"}; } } ``` @@ -4015,7 +4059,7 @@ puppeteer: 140. ### React-Intl چیه؟ - _React Intl_ یه کتابخونه برای راحت کردن کار با برنامههای چند زبانهست. این کتابخونه از مجموعهای از کامپوننتها و APIها برای فرمتبندی string، date و اعداد برای سهولت چندزبانگی استفاده میکنه. React Intl بخشی از _FormatJS_ هست که امکان اتصال به ریاکت رو با کامپوننتهای خودش فراهم میکنه. + _React Intl_ یه کتابخونه برای آسان نمودن توسعه برنامههای چند زبانهـست. این کتابخونه از مجموعهای از کامپوننتها و APIها برای فرمتبندی رشتهها، تاریخ و اعداد رو برای سادهسازی فرآیند چندزبانگی فراهم میکنه. React Intl بخشی از _FormatJS_ هست که امکان اتصال به ریاکت رو با کامپوننتهای خودش فراهم میکنه. **[فهرست](#فهرست)** diff --git a/book.pdf b/book.pdf index 3e90feb0..bdb05b6f 100644 Binary files a/book.pdf and b/book.pdf differ