השואת פתרונות תרגול מחזור חיים

תרגיל 1: `

const tickTimer = 3000;
const fastClicksPerSecond = 3;

function ClickRecorder(props)
{  
  return <button onClick={props.clickPressed}>Click me!</button>;
}

function Status(props)
{
  return <div>{props.clickCountInTick < fastClicksPerSecond ? 'Faster' : 'Not so fast'} - {props.clickCountInTick}</div>;
}

class App extends React.Component
{    

  constructor(props){
    super(props);
    
    this.state={ClicksOnTick:0};
  }
  
  componentWillMount()
  {
    this.timer = setInterval(this.tick,tickTimer);
  }
  
  clickPressed =()=>
  {
    this.setState((prevState) => ({ClicksOnTick : prevState.ClicksOnTick + 1}));
  }
  
  tick =()=>
  {
    this.setState({ClicksOnTick : 0});
  }
  
  render()
  {          
    return <div>
      <ClickRecorder clickPressed={this.clickPressed} />
      <Status clickCountInTick={this.state.ClicksOnTick} />
    </div>;
  }
}

ReactDOM.render(<App/>, document.getElementById('root'));

עופר תמשיך ככה אני לוקח את הפתרונות שלך כפתרונות דוגמא לקורס :slight_smile:

רק נקודה אחת - אל תשתמש ב componentWillMount. הוא נועד לקוד שלא משתמש בכתיב ה class כתחליף ל constructor. בקוד שמשתמש ב class יש לכתוב קוד רק ב constructor או ב componentDidMount. והחלוקה בין שניהם עובדת כך:

  1. אם זה קוד שרלוונטי לאתחול משתנים שאתה תצטרך את התוצאה שלו ל render הראשון, שים אותו ב constructor
  2. כל קוד אחר, לדוגמא חיבור לאירועים, טיימרים וכו׳ - דברים שלא צריך בשביל ה render הראשון אלא שההשפעה שלהם תורגש רק בהמשך - אותם שים ב componentDidMount.
לייק 1

פתרון תרגיל 3
אחרי הפסקה קלה חזרתי לתרגולים.
כתבתי קוד שעובד אבל אני לא כל כך בטוח למה הוא עובד…
מה שמוזר לי זה שבפונקציה changeIndex אני עושה SETSTATE.
למה הפונקציה הזו לא מאפסת את הערכים? הרי אני לא שומר את הערכים שהוקלדו באובייקט האבא…

class Box extends React.Component
{ 
  keyPressed = (e) =>
  {    
    this.me.innerText = e.key;
    
    this.props.changeIndex(e.target.tabIndex);
  }

  componentDidUpdate()
  {
    if (this.props.focus == true)
      {
        this.me.focus();
      }
  }
  
  render()
  {
    return <span 
           tabIndex={this.props.index} 
           className="box" 
           onKeyPress={this.keyPressed}
           ref={(me) => {this.me = me}}
            />;
  }
}

class App extends React.Component
{     
  
  constructor(props)
  {
    super(props);
    
    this.state={list:[{focus:true},
                      {focus:false},
                      {focus:false},
                      {focus:false}]
                }
    
  }
    
  changeIndex = (tabIndex) =>
  {
    const newList = this.state.list.slice();
    
    const newIndex = this.getNextIndex(newList.length,tabIndex);
    
    newList[tabIndex-1].focus=false;
    newList[newIndex-1].focus=true;
    
    this.setState({list:newList});
  }
  
  getNextIndex = (itemCount,currentIndex) =>
  {
  return ((currentIndex)%itemCount)+1;
  }
  
  render()
  {   
    
    const boxes = this.state.list.map(
      (item,index)=>
        <Box changeIndex={this.changeIndex}
          index={index+1}
          key={index}
          focus={item.focus}/>);
      
    return <div>
        <ul>{boxes}</ul>
    </div>;
  }
}

ReactDOM.render(<App/>, document.getElementById('root'));

CSS

.box {
    border: 1px solid gray;
    width: 80px;
    height: 80px;
    margin: 10px;
    display: inline-block;
    vertical-align: top;
    font-size: 30px;
    line-height: 80px;
    text-align: center;
}

כיף שחזרת,

הדבר המוזר הראשון בפיתרון שלך זה באמת שאתה לא שומר את הערכים עצמם בפקדים (וזו הסיבה ש setState לא מאפסת אותם). במקום זה אתה כותב את הערכים ישירות ל DOM עם innerText (ואגב מומלץ להשתמש ב textContent במקום ב innerText מאחר ו textContent נתמך ביותר דפדפנים).

לכן אם תתבקש להשתמש בערך הסופי שהמשתמש הכניס (כל 4 הספרות) למשל לשלוח את המספר לשרת - יהיה לך קשה להגיע למידע.

דבר שני זה שיש שהפיתרון מאוד עמוס. אני חושב שאפשר היה לגשת לזה עם הרבה פחות קוד. סתם לדוגמא במקום לשמור רשימה של אוביקטים עם focus לשמור באיזה אינדקס הפוקוס צריך להיות.

כמה הצעות לשיפורים כדי שנוכל להתקדם בקריאת הקוד:

  1. עדכן את המחלקה Box כך שתשמור את הסיפרה שהיא מייצגת כחלק מה State. השתמש ב State כדי לכתוב את הסיפרה למסך (במקום ב ref ו textContent).
  2. במחלקה App עדכן את הסטייט שיכיל רק את האינדקס של הקופסא שכרגע פעילה.
  3. לאחר מכן הוסף התמודדות עם מצב שמשתמש לוחץ עם העכבר על אחת הקופסאות כך שהפוקוס משתנה ועדכן את המשתנה המקביל ב App.

פרסם את הקוד לאחר הערות ונראה איך להמשיך

לייק 1

שמח לחזור, הייתה תקופה מעט עמוסה, מקווה שאחזור במהרה לתלם.`
הסיבה שרציתי להמנע משמירת הערך ב State היא שלא התבקשתי בשאלה, אז הנחתי שהדרך שלי
חסכונית מבחינת זיכרון.
מה שאני ממשיך לא להבין זה איך ייתכן שהפונקציה APP RENDER מופעלת על כל לחיצה, ולכן BOX אמור כל הזמן להיווצר מחדש והערכים להתאפס. אני לא מבין מה גורם לערכי ה BOX להישמר…

class Box extends React.Component
{  
  
  constructor(props)
  {
    super(props);
    
    this.state = {};
  }
  
  keyPressed = (e) =>
  {      
    this.setState({key:e.key});
    this.props.changeIndex(e.target.tabIndex);
  }

  mouseOnClick = (e) =>
  {
    this.props.changeIndex(e.target.tabIndex);
  }
  
  componentDidUpdate()
  {
    if (this.props.focus == true)
      {
        this.me.focus();
      }
  }
  
  render()
  {
    return <span 
           tabIndex={this.props.index} 
           className="box" 
           onKeyPress={this.keyPressed} 
           onClick={this.mouseOnClick}
           ref={(me) => {this.me = me}}>
              {this.state.key}
           </span>;
  }
}

class App extends React.Component
{     
  
  constructor(props)
  {
    super(props);
    
    this.state={focusIndex:0};
  }
    
  changeIndex = (tabIndex) =>
  {
    const newIndex = this.getNextIndex(this.props.boxNum,tabIndex);
    this.setState({focusIndex:newIndex});
  }
  
  indexChanged = (netIndex) =>
  {
    this.setState({focusIndex:netIndex});
  }
  
  getNextIndex = (itemCount,currentIndex) =>
  {
    return ((currentIndex+1)%itemCount);
  }
  
  render()
  {   
    var boxes = [];
    for (var i = 0; i < this.props.boxNum; i++) {
        const box = <Box  changeIndex={this.changeIndex}
                          mouseOnClick={this.indexChanged}
                          index={i}
                          key={i}
                          focus={i==this.state.focusIndex}/>;
        boxes.push(box);      
    } 
      
    return <div>
        <ul>{boxes}</ul>
    </div>;
  }
}

ReactDOM.render(<App boxNum={4}/>, document.getElementById('root'));

זאת בדיוק הנקודה-
הפוקנציה Render לא מציירת מחדש את האלמנט או מאפסת אותו. היא בסך הכל מחזירה לריאקט איזשהו ייצוג של האלמנט כמו שאתה חושב שהוא צריך להיות.

אם הייצוג הזה זהה למה שריאקט מכיר שמופיע על המסך אז שום שינוי לא יקרה במסך.

בגלל זה כשאתה משנה את הערך עם textContent ריאקט לא יודע שהערך השתנה ובקריאה הבאה לפונקציה render דברים נראים שנשארו אותו דבר, כך שאין שום שינוי על העמוד. זאת הסיבה שמומלץ להציג את כל המידע של הפקד דרך render ולא לכתוב ישירות ל DOM

לייק 1