הוספת אינטראקטיביות
כמה דברים על המסך מתעדכנים בתגובה לקלט המשתמש. לדוגמה, לחיצה על גלריית תמונות משנה את התמונה הפעילה. ב-React, נתונים המשתנים עם הזמן נקראים state. ניתן להוסיף מצב לכל רכיב, ולעדכן אותו לפי הצורך. בפרק זה, תלמד איך לכתוב רכיבים המטפלים באינטראקציות, לעדכן את הstate ולהציג פלט שונה לאורך זמן.
In this chapter
מגיבים לאירועים
תגיב יכול לך להוסיף מטפלי אירועים ל-JSX שלך. מטפלי אירועים הם פונקציות משלך שיופעלו בתגובה לאינטראקציות של משתמשים כמו לחיצה, ריחוף, הבדיקות בקלט טפסים וכן הלאה.
רכיבים מובנים כמו <button> תומכים רק באירועי דפדפן מובנים כמו onClick. עם זאת, אתה יכול גם ליצור רכיבים משלך, ולתת לprops שלהם למטפל באירועים כל שמות פרטים לאפליקציה שאתה אוהב.
export default function App() { return ( <Toolbar onPlayMovie={() => alert('Playing!')} onUploadImage={() => alert('Uploading!')} /> ); } function Toolbar({ onPlayMovie, onUploadImage }) { return ( <div> <Button onClick={onPlayMovie}> Play Movie </Button> <Button onClick={onUploadImage}> Upload Image </Button> </div> ); } function Button({ onClick, children }) { return ( <button onClick={onClick}> {children} </button> ); }
מצב: זיכרון של רכיב
רכיבים צריכים לעשות את מה שמופיע על המסך כמו מאינטראקציה. הקלדה בטופס אמורה לעדכן את שדה הקלט, לחיצה על “הבא” בקרוסלת תמונה אמורה לשנות איזו תמונה מוצגת, לחיצה על “קנה” מכניסה מוצר לסל הקניות. רכיבים צריכים “לזכור” דברים: ערך הקלט הנוכחית, התמונה הנוכחית, עגלת הקניות. ב-React, סוג זה של זיכרון ספציפי לרכיב נקרא מצב.
אתה יכול להוסיף מצב לרכיב עם useState Hook. הHooks הם פונקציות מיוחדות האפשרויות לרכיבים שלך להשתמש בתכונות React (מצב אחד מהתכונות הללו). ה- useState Hook יכול לך להכריז על ניהול מצב. זה לוקח את הstate ההתחלתי ומחזיר ערכים זוג: הstate הנוכחי ופונקציה קובעת מצב שמאפשרת לעדכן אותו.
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);הנה איך גלריית תמונות משתמשת ומעדכנת מצב בלחיצה:
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); const hasNext = index < sculptureList.length - 1; function handleNextClick() { if (hasNext) { setIndex(index + 1); } else { setIndex(0); } } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; return ( <> <button onClick={handleNextClick}> Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <button onClick={handleMoreClick}> {showMore ? 'Hide' : 'Show'} details </button> {showMore && <p>{sculpture.description}</p>} <img src={sculpture.url} alt={sculpture.alt} /> </> ); }
Ready to learn this topic?
קרא את מצב: זיכרון של רכיב כדי ללמוד לזכור ערך ולעדכן אותו באינטראקציה.
Read Moreעיבוד וביצוע
לפני שהרכיבים שלך יוצגו על המסך, הם חייבים להיות עובדים על ידי React. הבנת את החשבון שלך בפעולה זו תעזור לך על ביצוע הקוד ולהסביר את ההתנהלות.
דמיינו שהרכיבים שלכם הם מטבחים במטבח. בתרחיש זה, תגיב הוא המלצר שמגיש בקשות מלקוחות ומביא להם את ההזמנות שלהם. לתהליך זה של בקשה והגשה של ממשק משתמש יש שלושה שלבים:
- הפעלת עיבוד (משלוח הזמנת הסועד למטבח)
- עיבוד הרכיב (הכנת ההזמנה במטבח)
- מתחייב ל-DOM (ביצוע ההזמנה על השולחן)

Trigger 
Render 
Commit
Illustrated by Rachel Lee Nabors
Ready to learn this topic?
קרא את render and Commit כדי ללמוד את המחזור החיים של עדכון ממשק משתמש.
Read Moreמצב כתמונת מצב
צריך למשתני JavaScript רגילים, מצב תגובה מתנהג יותר כמו תמונת מצב. הגדר את זה לא משנה את הstate הstate יש לך, אלא מפעיל עיבוד מחדש. זה יכול להיות מפתיע בהתחלה!
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!התנהגות זו עוזרת לך להימנע מבאגים עדינים. הנה אפליקציית צ’אט קטנה. נסו לנחש מה קורה אם תלחצו תחילה על “שלח” ואחר כך תשנה את הנמען לבוב. שמו של מי יופיע ב’התראה’ חמש שניות מאוחר יותר?
import { useState } from 'react'; export default function Form() { const [to, setTo] = useState('Alice'); const [message, setMessage] = useState('Hello'); function handleSubmit(e) { e.preventDefault(); setTimeout(() => { alert(`You said ${message} to ${to}`); }, 5000); } return ( <form onSubmit={handleSubmit}> <label> To:{' '} <select value={to} onChange={e => setTo(e.target.value)}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> </select> </label> <textarea placeholder="Message" value={message} onChange={e => setMessage(e.target.value)} /> <button type="submit">Send</button> </form> ); }
Ready to learn this topic?
קרא את מצב כתמונת מצב כדי לדעת מה הstate נראה “קבוע” ולא מדורגות בתוך רופאי המקומות.
Read Moreתור לסדרה של עדכוני state
רכיב זה הוא באגי: לחיצה על “+3” מגדילה את הניקוד פעם אחת בלבד.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(score + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Score: {score}</h1> </> ) }
State as a Snapshot מסביר מדוע זה קורה. הגדרת בקשת עיבוד מחדש חדש, אך לא משנה אותו בקוד פועל. אז ‘ציון’ ממשיך להיות ‘0’ מיד אחרי שאתה קורא ל’setScore(score + 1)‘.
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0
setScore(score + 1); // setScore(0 + 1);
console.log(score); // 0אתה יכול לתקן זאת על העברת פונקציית עדכון בעת הגדרת הstate. שימו לב כיצד החלפת setScore(score + 1) ב-setScore(s => s + 1) מתקנת את כפתור “+3”. זה יכול לך להכנס לפי עדכוני מצב מרובים.
import { useState } from 'react'; export default function Counter() { const [score, setScore] = useState(0); function increment() { setScore(s => s + 1); } return ( <> <button onClick={() => increment()}>+1</button> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <h1>Score: {score}</h1> </> ) }
Ready to learn this topic?
קרא את לעמוד בתור סדרה של עדכוני state כדי ללמוד כיצד להעמיד את הרצף של עדכוני state בתור.
Read Moreעדכון אובייקטים בstate
מצב יכול לבנות כל סוג של ערך JavaScript, כולל אובייקטים. אבל אתה לא צריך לשנות אובייקטים ומערכים שאתה מחזיק בstate. במקום זאת, כאשר נך לעדכן אובייקט ומערך, עליך ליצור רצועת חדש (או ליצור עו של קיים), היכן לעדכן את הstate כדי להשתמש בעותק זה.
בדרך כלל, תשתמש בתחביר התפשטות ... כדי להעתיק אובייקטים ומערכים שברצונך לשנות. לדוגמה, עדכון אובייקט מקונן יכול להיראות כך:
import { useState } from 'react'; export default function Form() { const [person, setPerson] = useState({ name: 'Niki de Saint Phalle', artwork: { title: 'Blue Nana', city: 'Hamburg', image: 'https://i.imgur.com/Sd1AgUOm.jpg', } }); function handleNameChange(e) { setPerson({ ...person, name: e.target.value }); } function handleTitleChange(e) { setPerson({ ...person, artwork: { ...person.artwork, title: e.target.value } }); } function handleCityChange(e) { setPerson({ ...person, artwork: { ...person.artwork, city: e.target.value } }); } function handleImageChange(e) { setPerson({ ...person, artwork: { ...person.artwork, image: e.target.value } }); } return ( <> <label> Name: <input value={person.name} onChange={handleNameChange} /> </label> <label> Title: <input value={person.artwork.title} onChange={handleTitleChange} /> </label> <label> City: <input value={person.artwork.city} onChange={handleCityChange} /> </label> <label> Image: <input value={person.artwork.image} onChange={handleImageChange} /> </label> <p> <i>{person.artwork.title}</i> {' by '} {person.name} <br /> (located in {person.artwork.city}) </p> <img src={person.artwork.image} alt={person.artwork.title} /> </> ); }
אם העתקת להשתמש בספרים בקוד הופכת מייגעת, אתה יכול להשתמש בשימוש כמו Immer כדי להקטין קוד שחוזר על עצמו:
{ "dependencies": { "immer": "1.7.3", "react": "latest", "react-dom": "latest", "react-scripts": "latest", "use-immer": "0.5.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} }
Ready to learn this topic?
קרא את עדכון אובייקטים בstate כדי ללמוד כיצד לעדכן אובייקטים בצורה נכונה.
Read Moreעדכון מערכים בstate
מערכים הם סוג נוסף של אובייקטי JavaScript הניתנים לשינוי שאתה יכול לאחסן בstate וצריך להתייחס אליהם כאל קריאה בלבד. בדיוק כמו עם אובייקטים, כאשר אתה רוצה לעדכן מערך מאוחסן בstate, אתה צריך ליצור מערך חדש (או ליצור עותק של קיים), הוא להגדיר את הstate לשימוש במערך החדש:
import { useState } from 'react'; const initialList = [ { id: 0, title: 'Big Bellies', seen: false }, { id: 1, title: 'Lunar Landscape', seen: false }, { id: 2, title: 'Terracotta Army', seen: true }, ]; export default function BucketList() { const [list, setList] = useState( initialList ); function handleToggle(artworkId, nextSeen) { setList(list.map(artwork => { if (artwork.id === artworkId) { return { ...artwork, seen: nextSeen }; } else { return artwork; } })); } return ( <> <h1>Art Bucket List</h1> <h2>My list of art to see:</h2> <ItemList artworks={list} onToggle={handleToggle} /> </> ); } function ItemList({ artworks, onToggle }) { return ( <ul> {artworks.map(artwork => ( <li key={artwork.id}> <label> <input type="checkbox" checked={artwork.seen} onChange={e => { onToggle( artwork.id, e.target.checked ); }} /> {artwork.title} </label> </li> ))} </ul> ); }
אם העתקת מערכים בקוד הופכת מייגעת, אתה יכול להשתמש בספרייה כמו Immer כדי להקטין קוד שחוזר על עצמו:
{ "dependencies": { "immer": "1.7.3", "react": "latest", "react-dom": "latest", "react-scripts": "latest", "use-immer": "0.5.1" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }, "devDependencies": {} }
Ready to learn this topic?
קרא את עדכון מערכים בstate כדי ללמוד כיצד לעדכן מערכים בצורה נכונה.
Read Moreמה הלאה?
עברו אל מגיבים לאירועים כדי להתחיל לקרוא פרק זה עמוד אחר עמוד!
לחלופין, אם אתה כבר מכיר את הנושאים האלה, למה שלא תקרא על Managing State?