שיתוף state בין קומפונטות
לפעמים, אתה רוצה שstateם של שני רכיבים ישתנה תמיד ביחד. כדי לעשות זאת, להעביר אותו להורה המשותף הקרוב, הוא העביר אותו ביותר_3 באמצעות TK. זה ידוע בתור הרמת state למעלה וזה אחד הדברים הנפוצים ביותר שתעשו בכתיבת קוד React.
You will learn
- כיצד לחלוק מצב בין רכיבים על ידי הרמתו למעלה
- מהם רכיבים מבוקרים ובלתי מבוקרים
מצב הרמה למעלה לפי דוגמה
בדוגמה זו, רכיב ‘אקורדיון’ אב יוצר שני ‘פאנלים’ נפרדים:
אקורדיוןפאנלפאנל
לכל רכיב ‘פאנל’ יש מצב ‘isActive’ בוליאני שקובע אם התוכן שלו גלוי.
לחץ על כפתור הצג עבור שני הפאנלים:
import { useState } from 'react'; function Panel({ title, children }) { const [isActive, setIsActive] = useState(false); return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Show </button> )} </section> ); } export default function Accordion() { return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About"> With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city. </Panel> <Panel title="Etymology"> The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple. </Panel> </> ); }
שימו לב כיצד לחיצה על כפתור של לוח אחד לא משפיעה על הלוח השני - הם עצמאיים.


בתחילה, מצב ה-‘isActive’ של כל ‘פאנל’ הוא ‘שקר’, כך ששניהם נראים מכווצים


לחיצה על כפתורי ה’פאנל’ תעדכן רק את מצב ה’isActive’ של אותו ‘פאנל’ בלבד
אבל עכשיו נניח שאתה רוצה לשנות אותו כך שרק לוח אחד יורחב בכל זמן נתון. עם העיצוב הזה, הרחבת הפאנל השני אמורה לכווץ את הלוח הראשון. איך היית עושה את זה?
כדי לתאם את שני הלוחות הללו, עליך “להרים את מצבם” לרכיב אב בשלושה שלבים:
- הסר מצב ממרכיבי הצאצא.
- עבר נתונים מקודדים מההורה המשותף.
- הוסף מצב להורה המשותף והעביר אותו יחד עם מטפלי האירועים.
זה יאפשר לרכיב ‘אקורדיון’ לתאם את שני ה’פאנלים’ ולהרחיב רק אחד בכל פעם.
שלב 1: הסר מצב מהרכיבים הצאצאים
אתה תיתן שליטה ב-‘isActive’ של ה-Panel לרכיב האב שלו. המשמעות היא שרכיב העביר את ‘isActive’ ל’פאנל’ לפי props במקום. התחל על ידי הסרת השורה הזו מהרכיב ‘פאנל’:
const [isActive, setIsActive] = useState(false);ובמקום זאת, הוסף את ‘isActive’ לרשימת הprops של ה’פאנל’:
function Panel({ title, children, isActive }) {רכיב האב של הפאנל יכול לשלוט בisActive על ידי העברתו לפי אב. לעומת זאת, לרכיב Panel אין כרגע שליטה על הערך של isActive—זה תלוי עכשיו ברכיב האב!
שלב 2: העבר נתונים מקודדים קשיחים מההורה המשותף
כדי להעלות את הstate למעלה, עליך לאתר את הרכיב האב המשותף הקרוב ביותר של שני רכיבי הצאצא שברצונך לתאם:
אקורדיון(הורה המשותף הקרוב ביותר)פאנלפאנל
בדוגמה זו, זה רכיב ‘אקורדיון’. הוא יהפוך ל”מקור האמת” שעבורו הפאנל פעיל כרגע. הפוך את הרכיב ‘אקורדיון’ להעביר ערך מקודד של ‘isActive’ (לדוגמה, ‘true’) לשני הפאנלים:
import { useState } from 'react'; export default function Accordion() { return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About" isActive={true}> With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city. </Panel> <Panel title="Etymology" isActive={true}> The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple. </Panel> </> ); } function Panel({ title, children, isActive }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={() => setIsActive(true)}> Show </button> )} </section> ); }
נסה לערוך את ערכי ה-‘isActive’ המקודדים ברכיב ‘אדיון’ וראה את התוצאה על המסך.
שלב 3: הוסף מצב להורה המשותף
רמת מצב חוץ מזה שאתה עושה את האופי של מה מאחסן כstate.
במקרה זה, רק פאנל אחד צריך להיות פעיל בכל פעם. המשמעות היא שרכיב האב הנפוץ ‘אקורדיון’ צריך לעקוב אחר איזה פאנל הוא הפאנל הפעיל. במקום ערך ‘בוליאני’, הוא יכול להשתמש בפאפא עבור האינדקס של ה’נל’ הפעיל עבור ranking הstate:
const [activeIndex, setActiveIndex] = useState(0);כאשר activeIndex הוא 0, החלונית הראשונה פעילה, וכאשר היא 1, היא השנייה.
לחיצה על כפתור “הצג” בכל אחד מה’פאנלים’ צריכה לשנות את האינדקס הפעיל ב’אקורדיון’. פאנל לא יכול להגדיר את המצב K_1 לפי זה שהוא מוגדר בתוך האקורדיון. הרכיב ‘אקורדיון’ צריך לאפשר במפורש לרכיב ה’פאנל’ לשנות את המצב על ידי העברת מטפל באירועים כעזר:
<>
<Panel
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}
>
...
</Panel>
<Panel
isActive={activeIndex === 1}
onShow={() => setActiveIndex(1)}
>
...
</Panel>
</>ה-<button> בתוך ה-Panel ישתמש ב-‘onShow’ כמטפל באירוע קליק שלו:
import { useState } from 'react'; export default function Accordion() { const [activeIndex, setActiveIndex] = useState(0); return ( <> <h2>Almaty, Kazakhstan</h2> <Panel title="About" isActive={activeIndex === 0} onShow={() => setActiveIndex(0)} > With a population of about 2 million, Almaty is Kazakhstan's largest city. From 1929 to 1997, it was its capital city. </Panel> <Panel title="Etymology" isActive={activeIndex === 1} onShow={() => setActiveIndex(1)} > The name comes from <span lang="kk-KZ">алма</span>, the Kazakh word for "apple" and is often translated as "full of apples". In fact, the region surrounding Almaty is thought to be the ancestral home of the apple, and the wild <i lang="la">Malus sieversii</i> is considered a likely candidate for the ancestor of the modern domestic apple. </Panel> </> ); } function Panel({ title, children, isActive, onShow }) { return ( <section className="panel"> <h3>{title}</h3> {isActive ? ( <p>{children}</p> ) : ( <button onClick={onShow}> Show </button> )} </section> ); }
זה משלים את מצב הרמה למעלה! העברת הstate לרכיב האב המשותף אפשרה לך לתאם את שני הפאנלים. שימוש באינדקס הפעיל במקום שני דגלים “מוצג” הבטיח שרק פאנל אחד פעיל בזמן נתון. והעברת מטפל לילד אפשר לילד לשנות את מצב ההורה.


בתחילה, activeIndex של Accordion הוא 0, כך שהפאנל הראשון מקבל isActive = true


כאשר מצב activeIndex של Accordion משתנה ל1, הפאנל השני מקבל במקום זאת isActive = true
Deep Dive
מקובל לקרוא לרכיב עם state מקומית “לא מבוקר”. לדוגמה, רכיב ה-‘Panel’ המקורי עם ranking הstate ‘isActive’ אינו מבוקר מה שהאב שלו אינו יכול להשפיע אם הפאנל פעיל או לא.
לעומת זאת, אפשר לומר שרכיב “נשלט” כאשר הוא חייב מונע על ידי props ולא על ידי הstate המקומית שלו. זה יכול להכיל את ההתנהגות שלו. רכיב ה’פאנל’ הסופי עם הprops ‘isActive’ נשלט על ידי רכיב ה’אקורדיון’.
רכיבים לא מבוקרים קלים יותר לשימוש בתוך הוריהם שהם דורשים פחות תצורה. אבל הם פחות גמישים כשרוצים לתאם אותם יחד. רכיבים מבוקרים הם גמישים בצורה מקסימלית, אך הם דורשים מהן צריך להגדיר אותם לגמרי עם props.
בפועל, “נשלט” ו”בלתי מבוקר” קיפדים טכניים - לכל רכיב יש בדרך כלל שילוב מסוים של state מקומית ושל props. עם זאת, זו דרך שימושית לדבר על האופן שבו רכיבים מתוכננים והיכולות שהם מציעים.
בעת כתיבת רכיב, שקול איזה מידע בו יש לשלוט (באמצעות props), ואיזה מידע צריך להיות בלתי מבוקר (דרך הstate). אבל אתה תמיד יכול לשנות את דעתך ולהתחיל מחדש מאוחר יותר.
מקור אמת יחיד לכל state
ביישום React, לרכיבים רבים יהיו מצב משלהם. מצב מסוים עשוי “לחיות” קרוב לרכיבי העלים (רכיבים בתחתית העץ) כמו תשומות. מצב אחר עשוי “לגור” קרוב יותר לחלק העליון של האפליקציה. לדוגמה, אפילו ספריות ניתוב נוסף הלקוח מיו שמות בדרך כלל על ידי אחסון המסלול הנוכחי בstate React והעברתו על ידי props!
לעבור כל פיסת state ייחודית, תבחר את הרכיב ש”הבעלים” שלו. עיקרון זה ידוע גם כבעל “מקור אחד של אמת”. זה לא אומר שכל הstate חיה במקום אחד - אבל שלכל רכיב זה ישנה נתח מידע ו.במקום לשכפל מצב שותף אותו בין רכיבים*, להם שותפים אותו,*להם.
האפליקציה שלך תשתנה תוך כדי עבודה עליה. זה נפוץ שתזוז את הstate למטה או בחזרה למעלה בזמן שאתה עדיין מגלה היכן כל חלק של הstate “חי”. כל זה חלק מהתהליך!
כדי לראות איך זה מרגיש עם עוד כמה רכיבים, קרא את Thinking in React.
Recap
- כאשר אתה רוצה לתאם שני מרכיבים, העבר את מצבם להורה המשותף שלהם.
- לאחר שהעבירו את המידע דרך props מהורה המשותף שלהם.
- לבסוף, העבירו את מטפלי האירועים כדי שהילדים יוכלו לשנות את מצב ההורה.
- כדאי להתייחס לרכיבים כ”מבוקרים” (מונעים על ידי props) או “בלתי נשלטים” (מונעים על ידי state).
Challenge 1 of 2: כניסות מסונכרנות
שתי כניסות אלו אינן תלויות. לגרום להם להישאר מסונכרנים: עריכת קלט אחד אמורה לעדכן את הקלט השני באותו טקסט, ולהיפך.
import { useState } from 'react'; export default function SyncedInputs() { return ( <> <Input label="First input" /> <Input label="Second input" /> </> ); } function Input({ label }) { const [text, setText] = useState(''); function handleChange(e) { setText(e.target.value); } return ( <label> {label} {' '} <input value={text} onChange={handleChange} /> </label> ); }