رد فعل دورة تدريبية الجزء 27: مشروع الدورة

في هذا الجزء من ترجمة دورة React التدريبية ، يُطلب منك إنشاء مولد ميمي.

صورة

الجزء 1: نظرة عامة على الدورة ، وأسباب شعبية React ، ReactDOM و JSX
الجزء 2: المكونات الوظيفية
الجزء 3: ملفات المكونات ، هيكل المشروع
الجزء 4: مكونات الوالدين والطفل
الجزء 5: بدء العمل في تطبيق TODO ، أساسيات التصميم
الجزء 6: حول بعض ميزات الدورة ، JSX وجافا سكريبت
الجزء 7: الأنماط المضمنة
الجزء 8: مواصلة العمل على تطبيق TODO ، الإلمام بخصائص المكونات
الجزء 9: خصائص المكون
الجزء 10: ورشة عمل حول العمل مع خصائص المكون والتصميم
الجزء 11: توليد العلامات الديناميكية وطريقة صفائف الخريطة
الجزء 12: ورشة العمل ، المرحلة الثالثة من العمل على طلب TODO
الجزء 13: المكونات القائمة على الفصل
الجزء 14: ورشة عمل حول المكونات القائمة على الفصل ، وحالة المكون
الجزء 15: ورش العمل الصحية المكونة
الجزء 16: المرحلة الرابعة من العمل على طلب TODO ، التعامل مع الأحداث
الجزء 17: المرحلة الخامسة من العمل على تطبيق TODO ، وتعديل حالة المكونات
الجزء 18: المرحلة السادسة من العمل على طلب TODO
الجزء 19: طرق دورة حياة المكون
الجزء 20: الدرس الأول في التقديم الشرطي
الجزء 21: الدرس الثاني وورشة العمل حول التقديم الشرطي
الجزء 22: المرحلة السابعة من العمل على تطبيق TODO ، وتنزيل البيانات من مصادر خارجية
الجزء 23: الدرس الأول حول العمل مع النماذج
الجزء 24: نماذج الدرس الثاني
الجزء 25: ورشة عمل حول العمل مع النماذج
الجزء 26: بنية التطبيق ، نمط الحاوية / المكون
الجزء 27: مشروع المقرر

الدرس 45. مشروع المقرر. ميمي مولد


الأصل

لذلك وصلنا إلى مشروع الدورة. دعونا إنشاء تطبيق من شأنه أن يولد الميمات. لنبدأ بمشروع إنشاء تطبيق رد فعل قياسي تم إنشاؤه باستخدام هذا الأمر:

npx create-react-app meme-generator 

هنا يمكنك العثور على معلومات حول ميزات استخدامه.

أثناء العمل في هذا المشروع ، سيُطلب منك تنفيذ بعض أجزائه بنفسك ، ثم قراءة التفسيرات المتعلقة بها. يحتوي المشروع القياسي بالفعل على رمز App.js الموجود ، على وجه الخصوص ، في App.js index.js و App.js يمكنك إزالة هذا الرمز تمامًا ومحاولة كتابته بنفسك من أجل اختبار نفسك في تنفيذ الآليات القياسية لتطبيقات React.

في هذا المشروع ، أنت مدعو لاستخدام الأنماط التالية:

 * {   box-sizing: border-box; } body {   margin: 0;   background-color: whitesmoke; } header {   height: 100px;   display: flex;   align-items: center;   background: #6441A5;  /* fallback for old browsers */   background: -webkit-linear-gradient(to right, #2a0845, #6441A5);  /* Chrome 10-25, Safari 5.1-6 */   background: linear-gradient(to right, #2a0845, #6441A5); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ } header > img {   height: 80%;   margin-left: 10%; } header > p {   font-family: VT323, monospace;   color: whitesmoke;   font-size: 50px;   margin-left: 60px; } .meme {   position: relative;   width: 90%;   margin: auto; } .meme > img {   width: 100%; } .meme > h2 {   position: absolute;   width: 80%;   text-align: center;   left: 50%;   transform: translateX(-50%);   margin: 15px 0;   padding: 0 5px;   font-family: impact, sans-serif;   font-size: 2em;   text-transform: uppercase;   color: white;   letter-spacing: 1px;   text-shadow:       2px 2px 0 #000,       -2px -2px 0 #000,       2px -2px 0 #000,       -2px 2px 0 #000,       0 2px 0 #000,       2px 0 0 #000,       0 -2px 0 #000,       -2px 0 0 #000,       2px 2px 5px #000; } .meme > .bottom {   bottom: 0; } .meme > .top {   top: 0; } .meme-form {   width: 90%;   margin: 20px auto;   display: flex;   justify-content: space-between; } .meme-form > input {   width: 45%;   height: 40px; } .meme-form > button {   border: none;   font-family: VT323, monospace;   font-size: 25px;   letter-spacing: 1.5px;   color: white;   background: #6441A5; } .meme-form > input::-webkit-input-placeholder { /* Chrome/Opera/Safari */ font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input::-moz-placeholder { /* Firefox 19+ */ font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input:-ms-input-placeholder { /* IE 10+ */ font-family: VT323, monospace; font-size: 25px; text-align: cen } .meme-form > input:-moz-placeholder { /* Firefox 18- */ font-family: VT323, monospace; font-size: 25px; text-align: cen } 

يمكن تضمين هذه الأنماط في ملف index.css بالفعل في المشروع index.css في ملف index.js .

لذلك ، بناءً على افتراض أن ملفات index.js و App.js فارغة الآن ، فأنت بصفتك المهمة الأولى ، مدعوة لكتابة رمز index.js ، App.js بإنشاء أبسط مكون في App.js إلى index.js .

إليك ما يجب أن يظهر في index.js :

 import React from "react" import ReactDOM from "react-dom" import './index.css' import App from "./App" ReactDOM.render(<App />, document.getElementById("root")) 

نحن هنا نستورد ReactDOM و ReactDOM ، ReactDOM الأنماط من index.css App . بعد ذلك ، باستخدام طريقة ReactDOM.render() ، نقوم ReactDOM.render() ما ReactDOM.render() مكون App في عنصر صفحة index.html بمعرف root ( <div id="root"></div> ).

إليك ما قد يبدو App.js ملف App.js :

 import React from "react" function App() {   return (       <h1>Hello world!</h1>   ) } export default App 

هنا قدم الآن أبسط عنصر وظيفي.

في هذه المرحلة ، يبدو المشروع كما هو موضح أدناه.


تطبيق في المتصفح

قم الآن بإنشاء مكونين جديدين ، في ملفين تتطابق أسماؤهما مع أسماء المكونات:

  • مكون Header الذي سيتم استخدامه لعرض رأس التطبيق.
  • مكون MemeGenerator ، والذي سيتم فيه حل المهام الرئيسية المخصصة للتطبيق. وهي ، سيتم إجراء مكالمات إلى API هنا. سيتم تخزين بيانات التطبيق هنا.

النظر في الوظائف التي يتم تعيينها لهذه المكونات ، والتفكير في ما ينبغي أن تكون.

فيما يلي محتويات ملف Header.js :

 import React from "react" function Header() {   return (       <h1>HEADER</h1>   ) } export default Header 

نظرًا لأن هذا المكون سيتم استخدامه فقط لعرض رأس التطبيق ، فقد قمنا بتصميمه كمكون وظيفي.

هنا هو رمز ملف MemeGenerator.js :

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state ={}   }     render() {       return (           <h1>MEME GENERATOR SECTION</h1>       )   } } export default MemeGenerator 

هنا ، مع الأخذ في الاعتبار المهام التي من المفترض حلها عن طريق مكون MemeGenerator ، سوف نستخدم مكونًا يعتمد على الفصل. يوجد مُنشئ هنا يمكننا من خلاله تهيئة الحالة بكائن فارغ.

بعد إنشاء هذه الملفات ، App.js في App.js الترميز من المكون الوظيفي App.js ، والذي يستخدم مثيلات هذه المكونات ، دون أن ننسى أنه إذا قام المكون الوظيفي بإرجاع عدة عناصر ، فيجب أن يتم لفها في شيء ما. في حالتنا ، هذه هي <div> . هنا هو رمز App.js المحدث:

 import React from "react" import Header from "./Header" import MemeGenerator from "./MemeGenerator" function App() {   return (       <div>           <Header />           <MemeGenerator />       </div>   ) } export default App 

تحقق من مظهر التطبيق.


تطبيق في المتصفح

الآن دعونا نعمل على مكون Header . هنا سنستخدم العنصر الدلالية HTML5 <header> . سوف تحتوي هذه العلامة على الصورة والنص. الآن Header.js رمز ملف Header.js كما يلي:

 import React from "react" function Header() {   return (       <header>           <img               src="http://www.pngall.com/wp-content/uploads/2016/05/Trollface.png"               alt="Problem?"           />           <p>Meme Generator</p>       </header>   ) } export default Header 

إليك كيفية تغيير مظهر التطبيق.


تطبيق في المتصفح

تم تصميم عنوان التطبيق وفقًا للأنماط index.js مسبقًا في index.js . اكتمل الآن العمل على مكون Header .

سوف نستمر في التعامل مع مكون MemeGenerator . تمت دعوتك الآن لتهيئة حالة هذا المكون بشكل مستقل عن طريق كتابة البيانات التالية عليه:

  • النص المعروض في الجزء العلوي من ميمي (خاصية النص العلوي).
  • النص المعروض في أسفل meme (خاصية bottomText ).
  • صورة عشوائية (خاصية randomImage تحتاج إلى تهيئة مع الرابط http://i.imgflip.com/1bij.jpg ).

هذا هو ما سيكون رمز MemeGenerator.js بعد تهيئة الحالة:

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg"       }   }     render() {       return (           <h1>MEME GENERATOR SECTION</h1>       )   } } export default MemeGenerator 

الآن لن يتأثر مظهر التطبيق.

سوف نستخدم المكالمات إلى واجهة برمجة التطبيقات ، والتي تعرض مجموعة من الكائنات التي تحتوي على روابط للصور ، بناءً على الميمات التي يمكن إنشاؤها. في هذه المرحلة من العمل في المشروع ، فأنت مدعو لتنفيذ الوظيفة التالية في مكون MemeGenerator :

  • استدعاء API https://api.imgflip.com/get_memes/ .
  • احفظ البيانات المتوفرة في الاستجابة كصفيف من response.data.memes في خاصية الحالة الجديدة ( allMemeImgs ).

هنا ، لجعلها أكثر وضوحًا ، تم إرجاع جزء من بيانات JSON عند الوصول إلى واجهة برمجة التطبيقات هذه:

 {   "success":true,  "data":{      "memes":[         {           "id":"112126428",           "name":"Distracted Boyfriend",           "url":"https:\/\/i.imgflip.com\/1ur9b0.jpg",           "width":1200,           "height":800,           "box_count":3        },        {           "id":"87743020",           "name":"Two Buttons",           "url":"https:\/\/i.imgflip.com\/1g8my4.jpg",           "width":600,           "height":908,           "box_count":2        },        {           "id":"129242436",           "name":"Change My Mind",           "url":"https:\/\/i.imgflip.com\/24y43o.jpg",           "width":482,           "height":361,           "box_count":2        },        ….  ]  } } 

لحل المشكلة المذكورة أعلاه ، من الضروري مراعاة حقيقة أننا نتحدث عن البيانات التي يحتاجها المكون في بداية التطبيق.

لذلك ، لتحميلها ، سوف نلجأ إلى دورة حياة componentDidMount() . نحن هنا ، باستخدام طريقة fetch() القياسية fetch() ، نسمي API. يعود الوعد. بعد تحميل البيانات ، سيكون كائن الاستجابة متاحًا لنا ، memes مجموعة memes منه ونضعها في خاصية الحالة الجديدة allMemeImgs ، التي تمت تهيئتها باستخدام صفيف فارغ. نظرًا لعدم استخدام هذه البيانات بعد لتكوين شيء معروض على الشاشة ، سنقوم بطباعة العنصر الأول للصفيف على وحدة التحكم للتحقق من التشغيل الصحيح لآلية تحميل البيانات.

هنا هو رمز مكون MemeGenerator في هذه المرحلة من العمل:

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg",           allMemeImgs: []       }   }     componentDidMount() {       fetch("https://api.imgflip.com/get_memes")           .then(response => response.json())           .then(response => {               const {memes} = response.data               console.log(memes[0])               this.setState({ allMemeImgs: memes })           })   }     render() {       return (           <h1>MEME GENERATOR SECTION</h1>       )   } } export default MemeGenerator 

هذا هو ما يحصل على وحدة التحكم بعد تحميل البيانات بنجاح.


التطبيق في المتصفح ، الإخراج إلى وحدة التحكم للعنصر الأول من مجموعة تحميلها

لاحظ أن الصورة موصوفة باستخدام العديد من الخصائص. سنستخدم خاصية url فقط ، والتي تتيح الوصول إلى الرابط لتنزيل الصورة.

في بداية الدورة ، تحدثنا عن كيفية ظهور هذا التطبيق.


ميمي مولد

على وجه الخصوص ، تحتوي الواجهة على مجالين لإدخال النص ، والذي سيتم عرضه في الأجزاء العلوية والسفلية من الصورة. الآن أنت مدعو لاتخاذ ، استنادا إلى التعليمات البرمجية المحدثة لمكون MemeGenerator الموضح أدناه ، والذي يختلف عن الرمز أعلاه لهذا المكون في أن يضاف نموذج فارغ هنا ، لإنشاء اثنين من حقول النص ، topText و bottomText . ضع في اعتبارك أنه يجب إدارة هذه المكونات. أضف السمات الضرورية لهم. قم بإنشاء onChange أحداث onChange لهذه الحقول التي تحتاج فيها إلى تحديث خصائص الحالة المقابلة عند إدخال نص فيها.

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg",           allMemeImgs: []       }   }     componentDidMount() {       fetch("https://api.imgflip.com/get_memes")           .then(response => response.json())           .then(response => {               const {memes} = response.data               this.setState({ allMemeImgs: memes })           })   }     render() {       return (           <div>               <form className="meme-form">                   {                       //                        }                                 <button>Gen</button>               </form>           </div>       )   } } export default MemeGenerator 

بالمناسبة ، يجب الانتباه إلى أنه من أجل تضمين تعليق في الكود الذي تم إرجاعه بواسطة طريقة render() ، قمنا بتضمينه بين قوسين مجعدين للإشارة إلى النظام بأنه يجب تفسير هذه القطعة على أنها كود JavaScript.

إليك ما يجب أن تحصل عليه في هذه المرحلة من العمل على التطبيق:

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg",           allMemeImgs: []       }       this.handleChange = this.handleChange.bind(this)   }     componentDidMount() {       fetch("https://api.imgflip.com/get_memes")           .then(response => response.json())           .then(response => {               const {memes} = response.data               this.setState({ allMemeImgs: memes })           })   }     handleChange(event) {       const {name, value} = event.target       this.setState({ [name]: value })   }     render() {       return (           <div>               <form className="meme-form">                   <input                       type="text"                       name="topText"                       placeholder="Top Text"                       value={this.state.topText}                       onChange={this.handleChange}                   />                   <input                       type="text"                       name="bottomText"                       placeholder="Bottom Text"                       value={this.state.bottomText}                       onChange={this.handleChange}                   />                                 <button>Gen</button>               </form>           </div>       )   } } export default MemeGenerator 

الآن ستظهر صفحة التطبيق كالصفحة الموضحة أدناه.


تطبيق في المتصفح

بينما يتم عرض الحقول التي تحتوي على نص التعليمات فقط ، فإن إدخال البيانات فيها لا يؤدي إلى تغييرات في الواجهة. للتحقق من التشغيل الصحيح للآليات المطبقة هنا ، يمكنك استخدام الأمر console.log() .

الآن سنعمل على جزء التطبيق المسؤول عن عرض صورة meme. تذكر أنه لدينا الآن مجموعة تحتوي على معلومات حول الصور التي تم التخطيط لاستخدامها كأساس للميمات. يجب على التطبيق ، بالضغط على الزر Gen ، تحديد صورة بشكل عشوائي من هذه المجموعة وتكوين ميمي.

هنا هو رمز محدث لمكون MemeGenerator . هنا ، في طريقة render() ، أسفل رمز وصف النموذج ، يوجد عنصر <div> ، يتضمن عنصر <img> يعرض صورة ، واثنين من عناصر <h2> تعرض الملصقات. تم تصميم عنصري <div> و <h2> باستخدام الأنماط التي أضفناها إلى المشروع في بداية العمل عليه.

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg",           allMemeImgs: []       }       this.handleChange = this.handleChange.bind(this)   }     componentDidMount() {       fetch("https://api.imgflip.com/get_memes")           .then(response => response.json())           .then(response => {               const {memes} = response.data               this.setState({ allMemeImgs: memes })           })   }     handleChange(event) {       const {name, value} = event.target       this.setState({ [name]: value })   }     render() {       return (           <div>               <form className="meme-form">                   <input                       type="text"                       name="topText"                       placeholder="Top Text"                       value={this.state.topText}                       onChange={this.handleChange}                   />                   <input                       type="text"                       name="bottomText"                       placeholder="Bottom Text"                       value={this.state.bottomText}                       onChange={this.handleChange}                   />                                 <button>Gen</button>               </form>               <div className="meme">                   <img align="center" src={this.state.randomImg} alt="" />                   <h2 className="top">{this.state.topText}</h2>                   <h2 className="bottom">{this.state.bottomText}</h2>               </div>           </div>       )   } } export default MemeGenerator 

إليك ما يبدو عليه التطبيق الآن.


تطبيق في المتصفح

انتبه إلى حقيقة أن الصورة التي بدأت في تهيئة الحالة معروضة هنا. نحن لا نستخدم الصور المخزنة في خاصية حالة allMemeImgs . دعنا نحاول إدخال شيء في حقول النص.


تطبيق في المتصفح

كما ترون ، فإن الأنظمة الفرعية للتطبيق المسؤولة عن العمل مع النص تعمل كما هو متوقع. يبقى الآن فقط التأكد من أنه بالضغط على الزر Gen ، يتم اختيار صورة عشوائية من الصفيف مع بيانات الصورة وتحميلها في عنصر <img> ، الموجود في الصفحة أسفل حقول إدخال النص.

لتجهيز التطبيق بهذه الميزة - قم بتنفيذ المهمة التالية. قم بإنشاء طريقة تشتعل عند النقر فوق الزر Gen يجب أن تحدد هذه الطريقة إحدى الصور التي يتم تخزين المعلومات حولها في خاصية الحالة allMemeImgs ، ثم تقوم بتنفيذ الإجراءات التي تسمح لك بعرض هذه الصورة في عنصر <img> الموجود أسفل حقول إدخال النص. allMemeImgs في allMemeImgs أن allMemeImgs يخزن مجموعة من الكائنات التي تصف الصور ، وأن كل كائن من هذه الصفيف لديه خاصية url .

إليك الرمز الذي يوفر حلاً لهذه المشكلة:

 import React, {Component} from "react" class MemeGenerator extends Component {   constructor() {       super()       this.state = {           topText: "",           bottomText: "",           randomImg: "http://i.imgflip.com/1bij.jpg",           allMemeImgs: []       }       this.handleChange = this.handleChange.bind(this)       this.handleSubmit = this.handleSubmit.bind(this)   }     componentDidMount() {       fetch("https://api.imgflip.com/get_memes")           .then(response => response.json())           .then(response => {               const {memes} = response.data               this.setState({ allMemeImgs: memes })           })   }     handleChange(event) {       const {name, value} = event.target       this.setState({ [name]: value })   }     handleSubmit(event) {       event.preventDefault()       const randNum = Math.floor(Math.random() * this.state.allMemeImgs.length)       const randMemeImg = this.state.allMemeImgs[randNum].url       this.setState({ randomImg: randMemeImg })   }     render() {       return (           <div>               <form className="meme-form" onSubmit={this.handleSubmit}>                   <input                       type="text"                       name="topText"                       placeholder="Top Text"                       value={this.state.topText}                       onChange={this.handleChange}                   />                   <input                       type="text"                       name="bottomText"                       placeholder="Bottom Text"                       value={this.state.bottomText}                       onChange={this.handleChange}                   />                                 <button>Gen</button>               </form>               <div className="meme">                   <img align="center" src={this.state.randomImg} alt="" />                   <h2 className="top">{this.state.topText}</h2>                   <h2 className="bottom">{this.state.bottomText}</h2>               </div>           </div>       )   } } export default MemeGenerator 

يمكن تعيين الزر Gen معالج حدث يحدث عند النقر فوقه ، كما هو الحال مع أي أزرار أخرى. ومع ذلك ، نظرًا لحقيقة استخدام هذا الزر لإرسال النموذج ، سيكون من الأفضل استخدام onSubmit أحداث onSubmit للنموذج. في هذا المعالج ، handleSubmit() ، ندعو طريقة event.preventDefault() تأتي فيها لإلغاء إجراءات event.preventDefault() النموذج القياسي التي يتم خلالها إعادة تحميل الصفحة. بعد ذلك ، نحصل على رقم عشوائي في النطاق من 0 إلى القيمة المطابقة لمؤشر العنصر الأخير من مجموعة allMemeImgs ونستخدم هذا الرقم للإشارة إلى العنصر ذي الفهرس المقابل. بالانتقال إلى العنصر الذي هو الكائن ، نحصل على خاصية url لهذا الكائن randomImg خاصية الحالة randomImg . بعد ذلك ، يتم إعادة تقديم المكون ويتغير مظهر الصفحة.


صفحة التطبيق في المتصفح

اكتمل مشروع الدورة.

النتائج


في هذا الدرس ، قمت بإنشاء تطبيق يستخدم ما تعلمته أثناء إتقان React. في المرة القادمة سوف نتحدث عن تطوير تطبيقات React الحديثة ومناقشة أفكار المشروع ، والتنفيذ والتي يمكنك ممارسة العمل مع React.

أعزائي القراء! هل واجهت أي صعوبات أثناء إكمال هذا المشروع التدريبي؟

Source: https://habr.com/ru/post/ar447136/


All Articles