state: הזיכרון של קומפונטה
רכיבים צריכים לעשות את מה שמופיע על המסך כמו מאינטראקציה. הקלדה בטופס אמורה לעדכן את שדה הקלט, לחיצה על “הבא” בקרוסלת תמונה אמורה לשנות איזו תמונה מוצגת, לחיצה על “קנה” אמורה להכניס קוד לעגלת הקניות. רכיבים צריכים “לזכור” דברים: ערך הקלט הנוכחית, התמונה הנוכחית, עגלת הקניות. ב-React, סוג זה של זיכרון מיוחד לרכיב נקרא מצב.
You will learn
- איך להוסיף שינוי מצב עם ה-
useState - איזה צמד ערכים ה-‘useState’ מחזיר
- כיצד להוסיף יותר ממשתנה מצב אחד *למה state נקראת מקומית
כאשר מדרג רגיל אינו מספיק
הנה רכיב שמציג תמונה של פיסול. לחיצה על כפתור “הבא” אמורה להציג את הפסל הבא על ידי שינוי ה’אינדקס’ ל’1’, ואז ‘2’, וכן הלאה. עם זאת, זה לא יעבוד (תוכל לנסות את זה!):
import { sculptureList } from './data.js'; export default function Gallery() { let index = 0; function handleClick() { index = index + 1; } let sculpture = sculptureList[index]; return ( <> <button onClick={handleClick}> Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <img src={sculpture.url} alt={sculpture.alt} /> <p> {sculpture.description} </p> </> ); }
המטפל באירוע handleClick מעדכן ranking מקומי, index. אבל שני דברים מונעים מהשינוי הזה להיות גלוי:
- משתנים מקומיים לא נמשכים בין רינדור. כאשר React מעבד את הרכיב הזה פעם שנייה, הוא מעבד אותו מאפס - הוא לא מתחשב בשינויים במשתנים מקומיים.
- שינויים במשתנים מקומיים לא יפעילו עיבודים. React לא מבין שהיא צריכה לעבוד את הרכיב הזה עם החדשים.
כדי לעדכן רכיב בנתונים חדשים, צריכים לקרות שני דברים:
- שמור את הנתונים בין העיבודים.
- טריגר הגיבו לעיבוד הרכיב עם נתונים חדשים (עיבוד מחדש).
ה- useState Hook מספק את שני הדברים האלה:
- משתנה מצב לשמירה על הנתונים בין העיבודים.
- פונקציה קבועה מצב לעדכון השינוי ולהפעיל את React כדי לעבד שוב את הרכיב.
הוספת משתנה מצב
כדי להוסיף שינוי מצב, ייבא ‘useState’ מ-React בחלק העליון של הקובץ:
import { useState } from 'react';לאחר מכן, החלף את השורה הזו:
let index = 0;עִם
const [index, setIndex] = useState(0);index הוא משתנה מצב ו-setIndex הוא פונקציית הקובע.
התחביר
[ו]כאן נקרא destructuring array הוא יכול לקרוא ערכים ממערך. למערך המוחזר על ידי ‘useState’ יש תמיד בדיוק שני פריטים.
כך הם עובדים יחד בhandleClick:
function handleClick() {
setIndex(index + 1);
}כעת לחיצה על כפתור “הבא” משנה את הפסל הנוכחי:
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); function handleClick() { setIndex(index + 1); } let sculpture = sculptureList[index]; return ( <> <button onClick={handleClick}> Next </button> <h2> <i>{sculpture.name} </i> by {sculpture.artist} </h2> <h3> ({index + 1} of {sculptureList.length}) </h3> <img src={sculpture.url} alt={sculpture.alt} /> <p> {sculpture.description} </p> </> ); }
פגוש את ה-Hook הראשון שלך
ב-React, useState, כמו גם כל פונקציה שמתחילה ב-“use`”, נקראת Hook.
Hooks הם פונקציות מיוחדות שזמינות רק בזמן ש-React הוא rendering (עליהן ניכנס ביתר פירוט בעמוד הבא). הם מאפשרים לך “להתחבר” לתכונות שונות של React.
הstate היא רק אחת מהתכונות הללו, אבל תפגוש את הHooks אחרים מאוחר יותר.
האנטומיה של useState
אשרה קורא ל-useState, אתה אומר ל-React שאתה רוצה שהרכיב הזה יזכור משהו:
const [index, setIndex] = useState(0);במקרה זה, אתה רוצה ש-React תזכור את ‘אינדקס’.
הארגומנט היחיד ל-‘useState’ הוא הערך ההתחלתי של ranking הstate שלך. בדוגמה זו, הערך ההתחלתי של אינדקס מוגדר ל-0 עם useState(0).
בכל פעם שהרכיב שלך מעבד, ‘useState’ נותן לך מערך המכיל שני ערכים:
- שינוי הstate (
אינדקס) עם הערך ששמרת. - פונקציית ** מצב קובע** (
setIndex) יכול לעדכן את ranking הstate ולהפעיל את React לעיבוד הרכיב שוב.
הנה איך זה קורה בפעולה:
const [index, setIndex] = useState(0);- הרכיב שלך מעבד בפעם הראשונה. כשהעברת ‘0’ ל’useState’ כערך ההתחלתי של ‘index’, הוא יחזיר את ‘[0, setIndex]‘. React זוכר ש’0’ הוא ערך הstate האחרון.
- אתה מעדכן את הstate. כאשר משתמש לוחץ על הכפתור, הוא קורא
setIndex(index + 1).indexהוא0, אז זהsetIndex(1). זה אומר ל-React לזכור ש’אינדקס’ הוא ‘1’ עכשיו ומפעיל רינדור נוסף. - הרינדור השני של הרכיב שלך. React עדיין רואה את
useState(0), אך מה ש-React חפש שהגדרתאינדקסל-1, הוא מחזיר במקום[1, setIndex]. - וכן הלאה!
מתן משתני מצב מרובים לרכיב
אתה יכול לקבל כמה משתני מצב של כמה סוגים שאתה רוצה ברכיב אחד. לרכיב זה יש שני משתני מצב, ‘אינדקס’ מספר ו-‘showMore’ בוליאני שמופעל כאשר אתה לוחץ על “הצג פרטים”:
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); function handleNextClick() { setIndex(index + 1); } 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} /> </> ); }
זה רעיון טוב, כמו ‘אינדקס’ ו-‘showMore’ בדוגמה זו. אבל אם אתה מגלה שלעתים קשור אתה משנה שני משתני מצב ביחד, אולי יהיה קל יותר לשלב אותם לאחד. לדוגמה, אם יש לך מידע עם שדות רבים, נוח יותר לקבל את השינוי במצב יחיד שמכיל מעבר לשינוי מצב לכל שדה. קרא את Choosing the State Structure לקבלת עצות נוספות.
Deep Dive
אולי שמתם לב שהקריאה ‘TK_1__’ לא מקבלת שום מידע לגבי *__איזת. אין “מזהה” שמועבר לuseState, אז איך הוא יודע איזה ממשתני הstate להחזיר? האם זה מסתמך על קסם כמו ניתוח הפונקציות שלך? התשובה היא לא.
במקום זאת, כדי לאפשר את התחביר התמציתי שלהם, Hooks מסתמכים על סדר קריאה יציב על כל עיבוד של אותו רכיב. זה עובד היטב עם שפעלו לפי הכלל שלמעלה (“התקשרו רק ל-Hooks ברמה העליונה”), Hooks תמיד ייקרא באותו סדר. בנוסף, פלאגין linter תופס את רוב הטעויות.
באופן פנימי, React מחזיקה יותר של זוגות מצבים עבור כל רכיב. זה גם שומר על אינדקס זוגיות הנוכחית, שמוגדר ל-‘0’ לפני העיבוד. בכל פעם שאתה קורא ‘useState’, React נותן לך את הצמד הstates הבא ומגדיל את האינדקס. תוכל לקרוא עוד על המנגנון הזה ב-React Hooks: Not Magic, Just Arrays.
הדוגמה הזו לא משתמשת ב-React אבל היא נותנת לך מושג איך ‘useState’ עובד באופן פנימי:
let componentHooks = []; let currentHookIndex = 0; // How useState works inside React (simplified). function useState(initialState) { let pair = componentHooks[currentHookIndex]; if (pair) { // This is not the first render, // so the state pair already exists. // Return it and prepare for next Hook call. currentHookIndex++; return pair; } // This is the first time we're rendering, // so create a state pair and store it. pair = [initialState, setState]; function setState(nextState) { // When the user requests a state change, // put the new value into the pair. pair[0] = nextState; updateDOM(); } // Store the pair for future renders // and prepare for the next Hook call. componentHooks[currentHookIndex] = pair; currentHookIndex++; return pair; } function Gallery() { // Each useState() call will get the next pair. const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); function handleNextClick() { setIndex(index + 1); } function handleMoreClick() { setShowMore(!showMore); } let sculpture = sculptureList[index]; // This example doesn't use React, so // return an output object instead of JSX. return { onNextClick: handleNextClick, onMoreClick: handleMoreClick, header: `${sculpture.name} by ${sculpture.artist}`, counter: `${index + 1} of ${sculptureList.length}`, more: `${showMore ? 'Hide' : 'Show'} details`, description: showMore ? sculpture.description : null, imageSrc: sculpture.url, imageAlt: sculpture.alt }; } function updateDOM() { // Reset the current Hook index // before rendering the component. currentHookIndex = 0; let output = Gallery(); // Update the DOM to match the output. // This is the part React does for you. nextButton.onclick = output.onNextClick; header.textContent = output.header; moreButton.onclick = output.onMoreClick; moreButton.textContent = output.more; image.src = output.imageSrc; image.alt = output.imageAlt; if (output.description !== null) { description.textContent = output.description; description.style.display = ''; } else { description.style.display = 'none'; } } let nextButton = document.getElementById('nextButton'); let header = document.getElementById('header'); let moreButton = document.getElementById('moreButton'); let description = document.getElementById('description'); let image = document.getElementById('image'); let sculptureList = [{ name: 'Homenaje a la Neurocirugía', artist: 'Marta Colvin Andrade', description: 'Although Colvin is predominantly known for abstract themes that allude to pre-Hispanic symbols, this gigantic sculpture, an homage to neurosurgery, is one of her most recognizable public art pieces.', url: 'https://i.imgur.com/Mx7dA2Y.jpg', alt: 'A bronze statue of two crossed hands delicately holding a human brain in their fingertips.' }, { name: 'Floralis Genérica', artist: 'Eduardo Catalano', description: 'This enormous (75 ft. or 23m) silver flower is located in Buenos Aires. It is designed to move, closing its petals in the evening or when strong winds blow and opening them in the morning.', url: 'https://i.imgur.com/ZF6s192m.jpg', alt: 'A gigantic metallic flower sculpture with reflective mirror-like petals and strong stamens.' }, { name: 'Eternal Presence', artist: 'John Woodrow Wilson', description: 'Wilson was known for his preoccupation with equality, social justice, as well as the essential and spiritual qualities of humankind. This massive (7ft. or 2,13m) bronze represents what he described as "a symbolic Black presence infused with a sense of universal humanity."', url: 'https://i.imgur.com/aTtVpES.jpg', alt: 'The sculpture depicting a human head seems ever-present and solemn. It radiates calm and serenity.' }, { name: 'Moai', artist: 'Unknown Artist', description: 'Located on the Easter Island, there are 1,000 moai, or extant monumental statues, created by the early Rapa Nui people, which some believe represented deified ancestors.', url: 'https://i.imgur.com/RCwLEoQm.jpg', alt: 'Three monumental stone busts with the heads that are disproportionately large with somber faces.' }, { name: 'Blue Nana', artist: 'Niki de Saint Phalle', description: 'The Nanas are triumphant creatures, symbols of femininity and maternity. Initially, Saint Phalle used fabric and found objects for the Nanas, and later on introduced polyester to achieve a more vibrant effect.', url: 'https://i.imgur.com/Sd1AgUOm.jpg', alt: 'A large mosaic sculpture of a whimsical dancing female figure in a colorful costume emanating joy.' }, { name: 'Ultimate Form', artist: 'Barbara Hepworth', description: 'This abstract bronze sculpture is a part of The Family of Man series located at Yorkshire Sculpture Park. Hepworth chose not to create literal representations of the world but developed abstract forms inspired by people and landscapes.', url: 'https://i.imgur.com/2heNQDcm.jpg', alt: 'A tall sculpture made of three elements stacked on each other reminding of a human figure.' }, { name: 'Cavaliere', artist: 'Lamidi Olonade Fakeye', description: "Descended from four generations of woodcarvers, Fakeye's work blended traditional and contemporary Yoruba themes.", url: 'https://i.imgur.com/wIdGuZwm.png', alt: 'An intricate wood sculpture of a warrior with a focused face on a horse adorned with patterns.' }, { name: 'Big Bellies', artist: 'Alina Szapocznikow', description: "Szapocznikow is known for her sculptures of the fragmented body as a metaphor for the fragility and impermanence of youth and beauty. This sculpture depicts two very realistic large bellies stacked on top of each other, each around five feet (1,5m) tall.", url: 'https://i.imgur.com/AlHTAdDm.jpg', alt: 'The sculpture reminds a cascade of folds, quite different from bellies in classical sculptures.' }, { name: 'Terracotta Army', artist: 'Unknown Artist', description: 'The Terracotta Army is a collection of terracotta sculptures depicting the armies of Qin Shi Huang, the first Emperor of China. The army consisted of more than 8,000 soldiers, 130 chariots with 520 horses, and 150 cavalry horses.', url: 'https://i.imgur.com/HMFmH6m.jpg', alt: '12 terracotta sculptures of solemn warriors, each with a unique facial expression and armor.' }, { name: 'Lunar Landscape', artist: 'Louise Nevelson', description: 'Nevelson was known for scavenging objects from New York City debris, which she would later assemble into monumental constructions. In this one, she used disparate parts like a bedpost, juggling pin, and seat fragment, nailing and gluing them into boxes that reflect the influence of Cubism’s geometric abstraction of space and form.', url: 'https://i.imgur.com/rN7hY6om.jpg', alt: 'A black matte sculpture where the individual elements are initially indistinguishable.' }, { name: 'Aureole', artist: 'Ranjani Shettar', description: 'Shettar merges the traditional and the modern, the natural and the industrial. Her art focuses on the relationship between man and nature. Her work was described as compelling both abstractly and figuratively, gravity defying, and a "fine synthesis of unlikely materials."', url: 'https://i.imgur.com/okTpbHhm.jpg', alt: 'A pale wire-like sculpture mounted on concrete wall and descending on the floor. It appears light.' }, { name: 'Hippos', artist: 'Taipei Zoo', description: 'The Taipei Zoo commissioned a Hippo Square featuring submerged hippos at play.', url: 'https://i.imgur.com/6o5Vuyu.jpg', alt: 'A group of bronze hippo sculptures emerging from the sett sidewalk as if they were swimming.' }]; // Make UI match the initial state. updateDOM();
אתה לא צריך להבין את זה כדי להשתמש ב-React, אבל אתה יכול למצוא את זה מודל נפשי מועיל.
הstate מבודדת ופרטית
הstate הוא מקומי למופע רכיב על המסך. במילים אחרים, אם תעבדו את אותו רכיב, לכל עותק יהיה מצב מבודד לחלוטין! שינוי אחד מהם לא ישפיע על השני.
בדוגמה זו, הרכיב ‘גלריה’ ממקודם מוצג ללא שינויים בבלוגיקה שלו. נסה ללחוץ על הכפתורים בתוך כל אחת מהגלריות. שימו לב שstate שלה עצמאית:
import Gallery from './Gallery.js'; export default function Page() { return ( <div className="Page"> <Gallery /> <Gallery /> </div> ); }
זה מה שעושה מצב שונה ממשתנים רגילים שאתה יכול להצהיר בראש המודול שלך. הstate לא קשורה לקריאת פונקציה מסוימת או למקום בקוד, אלא היא “מקומית” למקום הספציפי על המסך. הצגת שני רכיבי <Gallery />, כך שstate שלהם מאוחסן בנפרד.
שימו לב גם איך רכיב ה’דף’ אינו “יודע” דבר על מצב ה’גלריה’ או אפילו אם יש לו כזה. נדרש לprops, מצב פרטי לחלוטין לרכיב המצהיר עליו. רכיב האב לא יכול לשנות אותו. זה יכול לך להוסיף מצב לכל רכיב או לכלול אותו כדי להשפיע על שאר הרכיבים.
מה אם היית רוצה ששתי הגלריות ישמרו על הstates שלהן מסונכרנים? הדרך הנכונה לעשות זאת ב-React. העמודים הבאים יעקבו בארגון מצב של רכיב בודד, אך נחזור לנושא זה ב-Sharing State Between Components.
Recap
- השתמש במשתנה מצב כאשר רכיב צריך “לזכור” מידע מסוים בין עיבודים.
- משתני מצב מוכרזים על ידי קריאה ל-
useStateHook. - ווים הם פונקציות מיוחדות שמתחילות ב’שימוש’. הם מאפשרים לך “להתחבר” לתכונות של React כמו מצב.
- הHooks לחייב להזכיר לך יבוא: צריך לקרוא אותם ללא תנאי. מתקשר לHooks, כולל ‘useState’, תקף רק ברמה העליונה של רכיב או Hook אחר.
- ה-
useStateHook מחזיר ערכים זוג: הstate הנוכחי והפונקציה לעדכן אותו. - אתה יכול לקבל יותר משינוי מצב אחד. באופן פנימי, React מתאים להם לפי ההזמנה שלהם.
- הstate היא פרטית לרכיב. אם תעבדו אותו בשני מקומות, כל עותק יקבל מצב משלו.
Challenge 1 of 4: השלם את הגלריה
כאשר אתה לוחץ על “הבא” על הפסל האחרון, הקוד קורס. תקן את ההיגיון כדי למנוע את ההתרסקות. אתה יכול לעשות זאת על ידי הוספת היגיון נוסף למטפל באירועים או על ידי השבתת הכפתור כאשר הפעולה אינה אפשרית.
לאחר תיקון ההתרסקות, הוסף כפתור “קודם” המציג את הפסל הקודם. זה לא אמור להתרסק על הפסל הראשון.
import { useState } from 'react'; import { sculptureList } from './data.js'; export default function Gallery() { const [index, setIndex] = useState(0); const [showMore, setShowMore] = useState(false); function handleNextClick() { setIndex(index + 1); } 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} /> </> ); }