שברים fractions בריאקט

היי מה נשמע? אז אני באמת אשלח מכאן ולא דרך המיילץ
בתרגיל השני של הפקדים בreactjs אני מנסה ששדה INPUT של השניות יסובב את שאר השדות ללא הצלחה מפני שאחרי כמה לחיצות אני כנראה מגיע לשבר גדול מדי ועדיין לא מצאתי דרך לעקוף או לפתור את הבעיה .
הנה החלק הרלוונטי:
import React from ‘react’;
import PropTypes from ‘prop-types’;
import DynamicNumber from ‘react-dynamic-number’;
export default class MyInput extends React.Component {
constructor(props) {
super(props);
this.state = { val: {seconds: 0,minutes:0,hours:0
} };

};

click = (e) => {
const val = this.state.val;
val.seconds = e.target.value/60 ;
val.minutes = e.target.value;
val.hours = e.target.value*60;
console.log(val);
{this.setState({val});}

};

render() {
return (

    <input type="number" onChange={this.click}  integer={10}

fraction={10}
positive={true} maxLength=“5” value={this.state.val.seconds}/>



);
}
}

הכי כיף כאן :slight_smile:

אגב הפורום תומך ב Markdown Syntax זה אומר שקטעי קוד כדאי לשים בתוך בלוק שהשורה הראשונה והאחרונה שלו הן שלושה סימני backtick כלומר ```. ואז זה נראה ככה:

import React from ‘react’;
import PropTypes from ‘prop-types’;
import DynamicNumber from ‘react-dynamic-number’;

export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      val: {seconds: 0,minutes:0,hours:0}
    };
  };

  click = (e) => {
    const val = this.state.val;
    val.seconds = e.target.value/60 ;
    val.minutes = e.target.value;
    val.hours = e.target.value*60;
    console.log(val);
    this.setState({val});
  };

  render() {
    return (
      <input type="number" onChange={this.click}  integer={10}
      fraction={10}
      positive={true} maxLength=“5” value={this.state.val.seconds}/>
    );
  }
}

עכשיו לגבי הקוד- יש לי שתי בעיות איתו ושתיהן קשורות ל State:

  1. State צריך להיות פשוט. לא מומלץ לשמור שם אוביקטים ולא מערכים. עדיף להישאר עם מספרים, מחרוזות ו Immutable Data.

  2. לא שומרים ב State ערכים נגזרים. מאחר ויש קשר בין השעות, הדקות והשניות מספיק לשמור רק ערך אחד מה-3. את השאר מחשבים ב render.

נסה להחליף את ה State כך שיחזיק מספר יחיד, וסביב זה לכתוב את הפיתרון. שים כאן את הפיתרון המעודכן ונתקדם איתו. גם אם זה לא עובד.

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

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0 };
   
  };
  
  click = (e) => {
    const val = e.target.value;
  
 
    {this.setState({val});}
    
    };
  
  
  render() {
 
  //console.log();
    return (
      <div >  
         
        <input type="number" onChange={this.click}  value={ this.state.val/60}/>    
        <input type="number" onChange={this.click}  value={ this.state.val}/>    
        <input type="number" onChange={this.click} value={ this.state.val*60}/>    
      </div>
    );
  }
}

כן אבל לפחות עכשיו אפשר לקרוא את זה :slight_smile:

עכשיו השאלה הבאה - מה val מייצג? את השניות? את הדקות? את השעות?

מצד אחד כשאני מסתכל על שלושת שדות ה input נראה שהוא מייצג את הדקות, כי שני השדות האחרים מציגים ערך המחושב מ val (חלקי שישים או כפול שישים)

אבל מצד שני בפונקציה onChange של שניות למשל מוגדר להפעיל את click. עכשיו click לוקחת את הערך מהשדה כמו שהוא ושומרת אותו ב val, לכן אם נעקוב אחרי זה:

  1. משתמש משנה את שדה השניות למשל כותב שם 60
  2. הפונקציה click נקראת ולוקחת את הערך 60 ושומרת אותו ב val
  3. עכשיו val התקלקל: הוא כבר לא מייצג את הדקות אלא את השניות…
  4. כשמגיעים שוב ל render צג השניות עדיין חושב ש val הוא מספר הדקות, מחלק ב-60 ומשנה את הערך ל-1.

כמו ש value שונה בין השדות כך גם onChnage צריך להתנהג אחרת ב-3 השדות.

נסה להמשיך מכאן ועדכן כשזה עובד (או עם הניסיון הבא)

אוקיי הנה ניסיון נוסף אולי זה נכון יותר מבחינה לוגית של ריאקט? בכל מהקרה ה’שעות’ עדיין תוקעות את ההרצה

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  hours = (e) => {
    const val = e.target.value;
    {this.setState({val});}
  };
    minutes = (e) => {
      const val = e.target.value;
      {this.setState({val});}
    };
      seconds = (e) => {
        const val = e.target.value;
        {this.setState({val});}
    };
    
  
  render() {

    return (
      <div >  
         
       
        <input type="number" onChange={this.hours}  value={ this.state.val/60}/>    
        <input type="number" onChange={this.minutes}  value={ this.state.val}/>    
        <input type="number" onChange={this.seconds}  value={ this.state.val*60}/>    
        
      </div>
    );
  }
}

מעולה!

רק תיקון קטן בתוך כל אחת מהפונקציות ה setState צריכה להעביר משהו אחר-
למשל הפונקציה hours שנקראת אחרי ששינו את מחוג השעות תצטרך להעביר ל state את val * 3600.

כי val מייצג את השניות אז כשמחוג השעות מראה 1 השניות צריכות להיות 3600

וזה בדיוק מסתדר עם הלוגיקה של ריאקט! מחזיקים מינימום מידע ב State וממנו גוזרים את כל השאר. בעת שינוי מסיקים איך השינוי משפיע על המידע השמור ואת זה שולחים חזרה לסטייט

משהו בסגנון הזה?

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  hours = (e) => {
    const hours = e.target.value;
    {this.setState({val:hours});}
  };
    minutes = (e) => {
      const minutes = e.target.value;
      {this.setState({val:minutes});}
    };
      seconds = (e) => {
        const seconds = e.target.value;
        {this.setState({val:seconds});}
    };
    
  
  render() {

    return (
      <div >  
         
       
        <input type="number" onChange={this.hours}  value={ this.state.val}/>    
        <input type="number" onChange={this.minutes}  value={ this.state.val*60}/>    
        <input type="number" onChange={this.seconds}  value={ this.state.val*3600}/>    
        
      </div>
    );
  }
}

אם כן נשארה רק הבעיה של הקורדינציה בינהם

כן רק תוסיף את החישוב, כלומר במקום לכתוב בתוך הפונקציה של השעות למשל:

const hours = e.target.value;

יש לכתוב:

const seconds = Number(e.target.value) * 3600;

כי e.target.value בכל פונקציה הוא הערך לפי השדה שקרא לפונקציה (למשל בפונקציה hours זה הערך בשעות), וצריך להמיר אותו לשניות שזה הערך שנשמר ב state

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

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  seconds = (e) => {
    
    const seconds = Number(e.target.value) * 3600
    {this.setState({val:seconds});}
  };
    minutes = (e) => {
      const minutes = Number(e.target.value)*60
      {this.setState({val:minutes});}
    };
    hours = (e) => {
        const hours =  Number(e.target.value) 
        {this.setState({val:hours});}
    };
    
  
  render() {

    return (
      <div >  
         
       
        <input type="number" onChange={this.hours}  value={ this.state.val}/>    
        <input type="number" onChange={this.minutes}  value={ this.state.val}/>    
        <input type="number" onChange={this.seconds}  value={ this.state.val}/>    
        
      </div>
    );
  }
}

וגם לא הבנתי את ההסבר האם הכוונה להגדיר את seconds בתוך פונקציה של hours ואת hours בתוך הפונקציה של seconds?

בלי לחץ… בוא ננסה כיוון אחר-

תחשוב על פקד ריאקט כמו קופסא שיש לה גרעין של ידע,
אבל אתה יכול להציץ עליו מכל מיני חורים והחורים באים בכל מיני צורות,
ולפי החור דרכו אתה מסתכל אתה רואה את הידע הזה בצורה אחרת או בצבע אחר
כאילו שיש שקף צבעוני שמכסה כל חור ואז מכיוון אחר זה נראה אדום ומכיוון אחר ירוק,
אבל זה אותו דבר בפנים

ככה לגבי השעון שלנו:
יש לו את val שמייצג את השניות,
אבל על ה val הזה אפשר להסתכל בכל מיני דרכים,
למשל אם מחלקים אותו ב-60 מקבלים מספר אחר,
שהוא הדקות,
אבל זה כמובן אותו מספר

הנה דוגמא של שעון שאי אפשר לשנות אותו - רק לראות את הערך,
וכל ערך שאתה כותב ב val מופיע ב-3 צורות שונות: שניות, דקות ושעות
(לחץ על הכפתור באמצע כדי לראות את הקוד ולהפעיל)


עכשיו מה קורה כשהערך הזה משתנה?
או יותר נכון איך קורה שהוא משתנה?

אז הפונקציה render היא הדרך לחשוף את הידע הפנימי של הפקד
כלפי חוץ
ולהציג אותו בכל מיני אופנים.
בנוסף אליה פקד יכול להגדיר Event Handlers שיופעלו כשאירוע מסוים קורה
הנה למשל הרחבה של הפקד הקודם שיכיל גם כפתור ו Event Handler שיופעל כשלוחצים על הכפתור,
כך שכשלוחצים על הכפתור יוגרל ערך חדש ל val:

בתרגיל שלנו Event Handlers מסוימים צריכים להתיחס גם למידע שהיה קשור לאירוע. למשל האירוע היה שינוי קלט בתיבת טקסט, ולכן ה Event Handler צריך לקחת את הקלט החדש שהוכנס.

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

מה קורה כאן? אז מבחינת הידע שוב יש לנו רק דבר אחד שצריך ״לדעת״ והוא הטקסט.
הטקסט מוצג בשתי דרכים: התיבה מציגה את הטקסט עצמו, והטקסט לידה מציג את האורך שלו,
אבל שני הדברים הם רק דרכים שונות להסתכל על אותו דבר - על הטקסט.

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

אבל זה לא חייב להיות כך,
כי למשל אפשר להחליט שאנחנו לא רוצים חלק מהאותיות
אולי אנחנו לא אוהבים את האות e אז כל פעם שמשתמש משנה
את הטקסט, אנחנו נמחק את כל ה e מהקלט לפני שנכניס אותו להיות
הסטייט החדש:

אבל רגע עוד לא סיימנו עם התיבות,
כי אפשר להוסיף עוד תיבת טקסט
שתשתמש ב Event Handler שונה:

התיבה השמאלית תמחק לך את ה e מהקלט,
התיבה הימנית לא
ובשני המקרים בסופו של דבר הקלט ייכנס ל state
ואז ישפיע על התיבה השניה וגם על הטקסט שמציג את האורך שלו

כלומר יש כאן שני תהליכים נפרדים: האחד קובע איך מידע
נכנס ל State דרך Event Handler
והשני קובע איך מידע ב State מוצג דרך הפונקציה render.

עכשיו בחזרה לשעון-
גם שם יש לנו מספר Event Handlers,
וכל אחד מתנהג קצת אחרת:

ה Event Handler שנקרא כשמשנים את ערך השעות למשל
ייקח את הערך החדש שהוא קיבל,
יכפיל אותו ב 3,600
וזה הערך שיכניס לסטייט.

ה Event Handler שנקרא כשמשנים את הדקות ייקח את המספר
יכפיל הפעם רק ב 60
ואת זה יכניס לסטייט

וה Event Handler שנקרא כשמשנים את השניות
פשוט שומר בסטייט בדיוק את הערך שקיבל
בלי לשנות אותו.

התהליך השני, ה render לוקח את הערך ומציג אותו ב-3 דרכים,
בדיוק כמו שכתבת:
פעם אחת מחלק ב 3,600 כדי להציג את הערך כ״שעות״
פעם שניה מחלק ב 60 כדי להציג את הערך ב״דקות״
ופעם שלישית משאיר כמו שהוא כדי להציג את ה״שניות״

וזה כל הסיפור עם סטייט בריאקט
סטייט נשמר ״בתוך״ הפקד
יוצא דרך render
ומשתנה דרך Event Handlers
(או מתוך Lifecycle Functions אבל זה כבר נושא להמשך הקורס :slight_smile:

יפה מאוד אני חושב שהגעתי לפריצת דרך לקח תודה על ההסבר המפורט!

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { seconds:0,minutes:0,hours:0};
  
  };
  
  seconds = (e) => {
    const val=Number(e.target.value);
    const seconds = val ;
    const minutes = val/60;
    const hours = val/3600;
    {this.setState({seconds,minutes,hours});}
  };
    minutes = (e) => {
      const val=Number(e.target.value);
      const seconds = val*60;
      const minutes = val;
      const hours = val/60;
      {this.setState({seconds,minutes,hours});}
    };
    hours = (e) => {
      const val=Number(e.target.value);
        const hours =  val;
        const minutes = val*60;
        const seconds = val*3600;
        {this.setState({seconds,minutes,hours});}
    };
    
  
  render() {

    return (
      <div >  
         
       
         
         seconds<input type="number" onChange={this.seconds}  value={ this.state.seconds}/>    
         minutes<input type="number" onChange={this.minutes}  value={ this.state.minutes}/>
         hours<input type="number" onChange={this.hours}  value={ this.state.hours}/>    
          
      </div>
    );
  }
}

איזה אושר! הפתרון נכון?

נראה עובד. עכשיו נשאר להוריד מהסטייט את השדות המיותרים - אתה שומר שם 3 ערכים, אבל מספיק אחד

בצורה כזאת? יופי זה עובד

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  seconds = (e) => {
    const val=Number(e.target.value);
    const seconds = val ;
    const minutes = val/60;
    const hours = val/3600;
    {this.setState({seconds,minutes,hours});}
  };
    minutes = (e) => {
      const val=Number(e.target.value);
      const seconds = val*60;
      const minutes = val;
      const hours = val/60;
      {this.setState({seconds,minutes,hours});}
    };
    hours = (e) => {
      const val=Number(e.target.value);
        const hours =  val;
        const minutes = val*60;
        const seconds = val*3600;
        {this.setState({seconds,minutes,hours});}
    };
    
  
  render() {

    return (
      <div >  
         
       
         
         seconds<input type="number" onChange={this.seconds}  value={ this.state.seconds}/>    
         minutes<input type="number" onChange={this.minutes}  value={ this.state.minutes}/>
         hours<input type="number" onChange={this.hours}  value={ this.state.hours}/>    
          
      </div>
    );
  }
}

זה הכיוון אבל עדיין בסטייט שמורים לך שלושה דברים…

ממ משהו בסגנון הזה?

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  seconds = (e) => {
    const val=Number(e.target.value);
    {this.setState({seconds:val,minutes:val/60,hours:val/3600});}
  };
    minutes = (e) => {
      const val=Number(e.target.value);
      {this.setState({seconds:val*60,minutes:val,hours: val/60});}
    };
    hours = (e) => {
      const val=Number(e.target.value);
        {this.setState({seconds:val,minutes:val*60,hours:val*3600});}
    };
    
  
  render() {

    return (
      <div >  
         
       
         
         seconds<input type="number" onChange={this.seconds}  value={ this.state.seconds}/>    
         minutes<input type="number" onChange={this.minutes}  value={ this.state.minutes}/>
         hours<input type="number" onChange={this.hours}  value={ this.state.hours}/>    
          
      </div>
    );
  }
}

הי,

כרגע יש לך 3 דברים בסטייט. אפשר לראות אותם בפונקציה render:

  1. הראשון this.state.seconds
  2. השני this.state.minutes
  3. השלישי this.state.hours

שלושתם נשמרים בתוך האוביקט this.state.

אבל הם קשורים אחד לשני,
ואין צורך לשמור את שלושתם.

תבחר אחד, למשל seconds,
ונסה לכתוב את כל התוכנית תוך שימוש ב this.state.seconds בלבד

כן… עכשיו אנחנו שם?

import React from 'react';
import PropTypes from 'prop-types';
export default class MyInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { val:0};
  
  };
  
  seconds = (e) => {
    const val=Number(e.target.value);
    {this.setState({seconds:val});}
  };
    minutes = (e) => {
      const val=Number(e.target.value);
      {this.setState({seconds:val*60});}
    };
    hours = (e) => {
      const val=Number(e.target.value);
        {this.setState({seconds:val*3600});}
    };
    
  
  render() {

    return (
      <div >  
         
       
         
         seconds<input type="number" onChange={this.seconds}  value={ this.state.seconds}/>    
         minutes<input type="number" onChange={this.minutes}  value={ this.state.seconds/60}/>
         hours<input type="number" onChange={this.hours}  value={ this.state.seconds/3600}/>    
          
      </div>
    );
  }
}
לייק 1

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