در جلسه قبل فصل اول را تمام کردیم و به شما 10 تمرین مختلف دادیم تا آن ها را حل کنید. امیدوارم که خودتان این تمرینات را حل کرده باشید. در این جلسه می خواهیم پاسخ این تمرینات را برای شما توضیح دهیم. لیست تمرینات را جهت یادآوری دوباره ذکر می کنم:
برای شروع کار در پوشه src دو پوشه دیگر به نام های UserInput و UserOutput ایجاد می کنیم. سپس درون این پوشه ها دو فایل به نام های UserInput.js و UserOutput.js می سازیم.
در جلسات قبلی یاد گرفتیم تنها زمانی از کامپوننت های کلاس-محور استفاده می کنیم که بخواهیم state را تغییر دهیم و باید سعی کنیم تا حد ممکن از کامپوننت های کاربردی (functional) استفاده کنیم. در تمریناتی که به شما دادیم نیازی به تغییر دادن state نیست بنابراین برای هر دو کامپوننت از نوع کاربردی استفاده خواهیم کرد.
تمرین 1 و 2 را با هم انجام می دهیم. برای شروع فایل UserInput را باز کرده و کد آن را بدین شکل می نویسیم:
import React from 'react'; const UserInput = () => { return <input type="text" /> } export default UserInput;
در تمرین گفته بودیم که UserInput باید یک input ساده نمایش دهد و این کدها نیز دقیقا همین کار را کرده اند.
نکته: اگر import کردن React را فراموش کنید کدهایتان خطا می دهند، چراکه React است که کد JSX را تبدیل به عناصر HTML می کند (تابع createElement) در غیر این صورت کدهای JSX و جاوا اسکریپت با هم قاطی می شوند. حالا فایل UserOutput را باز می کنیم. کدهای این فایل نیز ساده هستند:
import React from 'react'; const UserOutput = () => { return ( <div> <p>Some random text!</p> <p>I hope I'll be overwritten!</p> </div> ) } export default UserOutput;
حالا به تمرین شماره 3 می رسیم. برای چندین بار نمایش دادن (یا حتی یک بار نمایش دادن) کامپوننت UserOutput باید آن را در فایل App.js وارد کنیم بنابراین:
import UserInput from './UserInput/UserInput'; import UserOutput from './UserOutput/UserOutput';
اکنون می توانیم درون فایل App.js بگوییم:
class App extends Component { render() { return ( <div className="App"> <UserInput /> <UserOutput /> <UserOutput /> <UserOutput /> </div> ); } }
برای اینکه راحت تر ببینیم چه کارهایی باید انجام بدهیم، لیست تمرینات را بالای این کامپوننت ها نمایش می دهم:
class App extends Component { render() { return ( <div className="App"> <ol> <li>Create TWO new components: UserInput and UserOutput</li> <li>UserInput should hold an input element, UserOutput two paragraphs</li> <li>Output multiple UserOutput components in the App component (any paragraph texts of your choice)</li> <li>Pass a username (of your choice) to UserOutput via props and display it there</li> <li>Add state to the App component (=> the username) and pass the username to the UserOutput component</li> <li>Add a method to manipulate the state (=> an event-handler method)</li> <li>Pass the event-handler method reference to the UserInput component and bind it to the input-change event</li> <li>Ensure that the new input entered by the user overwrites the old username passed to UserOutput</li> <li>Add two-way-binding to your input (in UserInput) to also display the starting username</li> <li>Add styling of your choice to your components/ elements in the components - both with inline styles and stylesheets</li> </ol> <UserInput /> <UserOutput /> <UserOutput /> <UserOutput /> </div> ); } }
با ذخیره کردن این فایل ها و مراجعه به مرورگر می بینیم که خروجی ما صحیح و سالم نمایش داده می شود.
به تمرین شماره 4 می رسیم. باید با استفاده از props یک username را به UserOutput بفرستیم. بنابراین در فایل App.js یک username را به دلخواه پاس می دهیم:
<UserInput /> <UserOutput userName="Max" /> <UserOutput userName="Max" /> <UserOutput userName="Max" />
شما می توانید برای هر کدام از UserOutput ها یک مقدار متفاوت در نظر بگیرید (همانطور که در جلسات قبل نشان داده شد) اما ما اینجا همه را یکی می گذاریم. در ضمن انتظار دارم که بدانید userName می تواند هر مقدار دیگری مانند usrName یا username یا ... باشد. انتخاب نام با شما است. حالا به فایل UserOutput برمی گردیم تا از props استفاده کنیم:
const UserOutput = (props) => { return ( <div> <p>username: {props.userName}</p> <p>I hope I'll be overwritten!</p> </div> ) }
از آنجایی که در تمرینات نگفته بودم username را در چه قسمتی از کدها نمایش دهیم انتخاب کاملا با شماست. من دوست داشتم به این شکل آن را در پاراگراف اول نمایش دهم. با مراجعه به مرورگر باید نام Max را سه بار مشاهده کنیم:
به تمرین بعدی می رسیم؛ تعیین state! اگر یادتان باشد state فقط یک property در کلاس شما است. به همین دلیل به راحتی می توانیم آن را درون کلاس اضافه کنیم:
state = { username: 'supermax' }
نکته: شما می توانید property این شیء (یعنی username را) به هر شکلی که خواستید بنویسید (مثلا userName یا usname یا هر چیز دیگری). دلیل اینکه آن را به شکل username نوشتیم این است که آن را با prop اشتباه نگیریم.
حالا باید این مقدار را از state به کامپوننت UserOutput بدهیم، بنابراین:
<UserInput /> <UserOutput userName={this.state.username} /> <UserOutput userName={this.state.username} /> <UserOutput userName='Max' />
آخرین مورد را به صورت hardcode باقی میگذاریم تا با بقیه فرق داشته باشد. شما می توانید آن را هم از طریق state مدیریت کنید.
نوبت تمرین شماره 6 است؛ اضافه کردن یک متد برای دستکاری state (از نوع event-handler). ما می خواهیم هر زمان که کاربر در input چیزی وارد کرد، متد ما اجرا شود. ما نام متد خودمان را usernameChangedHandler می گذاریم:
usernameChangedHandler = () => { }
این یکی از روش های ایجاد متد در کلاس های جاوا اسکریپتی است. ممکن است بگوییم render نیز یک متد است اما از این روش استفاده نمی کند. حرف شما صحیح است! شما می توانید متدها را به این شکل نیز تعریف کنید:
usernameChangedHandler () { }
اما اگر بخواهید از کلیدواژه this و event-handler ها استفاده کنید (مانند پروژه فعلی ما)، کلیدواژه this در روش دوم به کلاس اشاره نخواهد کرد! و باعث بروز مشکلاتی می شود که حتما در مورد this شنیده اید. بنابراین بهتر است از روش arrow function ها استفاده کنید.
حالا باید کدهای متد را بنویسیم:
usernameChangedHandler = (event) => { this.setState({username: event.target.value}); }
ما می دانیم که قرار است یک شیء event دریافت کنیم (بر اساس تمرین ها، بعدا یک event-listener به عنصر input اضافه می کنیم) بنابراین آن را به عنوان آرگومان متد تعیین کرده و با متد setState مقدار State را تغییر می دهیم. مقدار event.target هدف رویداد را مشخص می کند. از آنجایی که رویداد ما روی یک عنصر input قرار می گیرد بنابراین هدف رویداد، عنصر input است که با اضافه کردن value مقدار تایپ شده در آن را به دست می آوریم.
تا اینجای کار یک متد تعریف کرده ایم که state را تغییر می دهد اما هنوز آن را فراخوانی نکرده ایم. بنابراین نوبت تمرین شماره 7 است! «متد event-handler را به عنوان reference به UserInput ارسال کرده و آن را به رویداد input-change بایند (bind) کنید.»
برای کامپوننت UserInput می گوییم:
<UserInput changed={this.usernameChangedHandler} />
باز هم گوشزد می کنم که نباید در آخر نام متد پرانتز اضافه کنید و گرنه متد بدون توجه به رویداد و به صورت خودکار (به محض render شدن DOM) اجرا خواهد شد.
به فایل UserInput بروید تا رویداد را مشخص کنیم:
const UserInput = (props) => { return <input type="text" onChange={props.changed} /> }
در مورد رویداد onChange قبلا صحبت کرده ایم. همچنین props.changed یعنی خصوصیت changed که قبلا برای UserInput تعیین کرده بودیم:
<UserInput changed={this.usernameChangedHandler} />
شما می توانید هر نامی که میخواهید به جای changed قرار دهید.
باید two way binding (بایندینگ دو طرفه) را اضافه کنیم. یعنی در هنگام لود شدن صفحه مقدار اولیه در input قرار بگیرد. برای این کار می گوییم:
<UserInput changed={this.usernameChangedHandler} currentName={this.state.username} />
یک property به نام currentName برای کامپوننت خود تعریف کرده ایم و مقدارش را برابر با مقدار username (در state) گذاشته ایم. با رفتن به فایل UserInput.js می توانیم به عنوان props از آن استفاده کنیم:
const UserInput = (props) => { return <input type="text" onChange={props.changed} value={props.currentName} /> }
جدا کردن کدها از هم فقط برای خوانا تر شدن کدها است. با مراجعه به مرورگر متوجه تغییر می شوید.
تمرین آخر نیز اضافه کردن style است. برای حل این تمرین از هر دو روش گفته شده در جلسات قبل استفاده می کنیم؛ برای UserInput که بدون عنصر نگهدارنده است از استایل های inline استفاده میکنیم تا مطمئن شویم استایل ها روی عناصر دیگر اعمال نمی شوند:
const UserInput = (props) => { const inputStyle = { border: '2px solid red' } return <input type="text" style={inputStyle} onChange={props.changed} value={props.currentName} /> }
برای UserOutput از فایل های css استفاده می کنیم؛ یک فایل به نام UserOutput.css بسازید و استایل های مورد نظرتان را در آن قرار دهید. استایل های ما این است:
.UserOutput { width: 60%; padding: 16px; margin: 16px; border: 2px solid black; background-color: #ccc; text-align: center; }
باید فایل استایل ها را وارد کامپوننت خود کنیم. وارد فایل UserOutput.js شوید و استایل ها را import کنید:
import './UserOutput.css';
حالا این کلاس را به یک عنصر می دهیم:
const UserOutput = (props) => { return ( <div className='UserOutput'> <p>username: {props.userName}</p> <p>I hope I'll be overwritten!</p> </div> ) }
(className در کد بالا استایل ها را دریافت کرده است)
چنانچه وارد مرورگر شویم استایل های خود را مشاهده خواهیم کرد:
به شما تبریک می گویم که تمرینات فصل اول را نیز به اتمام رساندید. از قسمت بعدی وارد فصل دوم این سری آموزشی می شویم و در آن فصل یاد می گیریم که چطور محتوای پویا را بر اساس شرط ها نمایش دهیم. همچنین در مورد نمایش انواع لیست ها صحبت خواهیم کرد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.