קורס React 2020 שיעור תרגול: גישה ישירה ל DOM

הי

אין צורך להשתמש ב componentDidUpdate בכלל. עדיף תמיד להשתמש ב Hooks וב useEffect.

מה ניסית להשיג בשתי השורות האלה:

    useEffect(() => focus(),[]);
    useEffect(() => focus());

?

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

אה עכשיו הבנתי - אז זה צריך להיות רשום כך:

// set initial focus when component mounts (componentDidMount)
useEffect(() => focus(), []);

// update focused element when component state changes (componentDidUpdate)
useEffect(() => focus(), [currentFocusIndex]);

ובכל מקרה נדמה לי שהקוד יעבוד גם בלי השורה הראשונה (כלומר בלי זאת שמטפלת בנפרד בעליה של הקומפוננטה)

ולגבי הלולאה בתוך הקומפוננטה יותר מקובל לכתוב כך:

<div style={{display:"flex"}} ref = {refDivs}>
    {
      (new Array(count)).fill(null).map((_, i) => (
          <div key={i} style = {style} tabIndex = {1} onKeyPress = {(e)=>onKeyPressHandler(e , i)}> {rectanglesInnerText[i]} </div>
      ))
    }
</div>

תודה על ההערות! והקוד אכן עובד בלי השורה הראשונה.

לייק 1

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

import React, { useRef, useState } from 'react';

export default function Targil1(props){

    const {inputCount} = props;

    const [inputsText, setInputsText] = useState(new Array(inputCount).fill(""));

    const [inputsRef, setInputsRef]   = useState(new Array(inputCount).fill(null));

    const divStyle = {

        "width":"20px",

        "height":"20px",

        "backgroundColor":"blue"

    }

    function ChangeValueAndFocus(index, e){

        inputsText[index] += e.key;

        index != inputCount-1 ? 

        inputsRef[index].current.nextSibling.focus() : inputsRef[0].current.focus();

        setInputsText(...inputsText);

        setInputsRef(...inputsRef);

    }

    return (

        <div>

            {

                inputsText.map((item, index) => (

                    <div key={index} 

                        style={divStyle}

                        textcontent={item}

                        tabIndex={1}

                        ref={inputsRef[index]}

                        onKeyPress={(e) => ChangeValueAndFocus(index, e)}>                     

                    </div>

                ))

            }

        </div>

    )

}

איך את יוצרת את הקומפוננטה? איך את מעבירה ערך ל
inputCount ?

שני מצבים בהם יווצר רק div אחד הם או אם inputCount הוא הערך 1 או אם inputCount הוא ערך שאינו מספר

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

הי,
אני רואה שאת משתמשת ב tabIndex אבל הוא זהה לכולם. אולי תנסי לשים tabIndex בסדר עולה כדי להתאים לסדר הטאבים הטבעי של המערכת.

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

אז יש לי שתי בעיות,

  1. אני עדיין לא מצליחה לגרום לפוקוס להיות אוטומטית על הדיב השמאלי
  2. גם אם אני עושה טאב במקלדת ומקלידה תו כלשהו אני מקבלת שגיאה Cannot read property ‘focus’ of null
    לא הצלחתי להבין למה
import React, { useRef, useState } from 'react';

export default function Targil1(props){

    const {inputCount} = props;

    const [inputsText, setInputsText] = useState(new Array(inputCount).fill(""));

    const inputsRef  = new Array(inputCount).fill(useRef(null));

    

    const divStyle = {

        "width":"40px",

        "height":"40px",

        "border":"double",

        "marginRight":"5px",

    }

    const containerDiv = {

        "display":"flex"

    }

    function ChangeValueAndFocus(index, e){

        inputsText[index] += e.key;

        index != inputCount-1 ? 

        inputsRef[index].current.nextSibling.focus() : inputsRef[0].current.focus();

        setInputsText(...inputsText);

    }

    return (

        <div style={containerDiv}>

            {

                inputsText.map((item, index) => (

                    <div key={index} 

                        autoFocus = {index === 0 ? true : false}

                        style={divStyle}

                        tabIndex={index}

                        ref={inputsRef[index]}                   

                        onKeyPress={(e) => ChangeValueAndFocus(index, e)}>

                            {item}                     

                    </div>

                ))

            }

        </div>

    )

}

הי
היו מספר תיקונים. שמרתי את כולם בקובץ כאן שתוכלי לראות ולהשוות:

בגדול:

  1. בשביל לשכפל מערך צריך להשתמש גם בסוגריים מרובעים (ולא רק בסימן שלוש נקודות), כלומר צריך להיות כתוב בתוך הפונקציה:
        setInputsText([...inputsText]);
  1. הקוד הזה שגוי:
    const inputsRef  = new Array(inputCount).fill(useRef(null));

זה באג מעניין: הקוד useRef מופעל רק פעם אחת והתוצאה שלו נכנסת ל fill. זאת אותה התוצאה שאיתה את ממלאת את כל המערך ולכן כל ה ref-ים שלך הם בעצם אותו ref (כי useRef נקרא רק פעם אחת).

  1. אם את כבר שומרת useRef לכל ה div-ים שמעניינים אותך, אפשר להשתמש בהם ולא צריך את useSibling (שאני לא בטוח למה לא עבד אבל בכל מקרה לא הייתי משתמש בו)

  2. לגבי הפוקוס באמת autoFocus לא רלוונטי ל div-ים, הוא עובד רק על אלמנטי קלט. במקומו יצרתי אפקט שבאמצעות קוד מפעיל focus על הדיב הראשון

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

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

אה כן אני רואה - useEffect הוא בדיוק בשיעור הבא:
https://www.tocode.co.il/bundles/react/lessons/useeffect

בשביל לפתור את הפוקוס הראשוני בלי useEffect אני חושב שחייבים להחליף את ה div ל input (ואז autoFocus יעבוד)

הכוונה שלי ב"פוקוס מקלדת משתנה מחוץ לפקד" היתה למקרים שמשתמש לוחץ Tab ואז הפוקוס עובר לא דרך הקוד שלך. הפיתרון שלך עם ref מטפל אוטומטית גם במקרה זה בגלל שכל לחיצת מקלדת את תמיד מסתכלת מאיזה div הגיעה הלחיצה ולפי זה מחליטה מי הבא בתור שמקבל את הפוקוס.
פיתרון אחר היה יכול להיות לשמור בתור משתנה State איזה div נמצא כרגע בפוקוס, ואז כן היה צריך טיפול מיוחד בלחיצות על Tab

שיניתי לפי מה שכתבת אבל זה נראה שמשום מה הוא לא מתייחס לשינוי הstate ולכן לא רואים שינוי בפוקוס אבל לא הצלחתי להבין למה?

import React, { useRef, useState } from 'react';

export default function Targil1(props){

    const {inputCount} = props;

    const [inputsText, setInputsText] = useState(new Array(inputCount).fill(""));

    const inputsRef  = [];

    for(let i = 0; i < inputCount; i++){

        inputsRef.push(useRef(null));

    }

    

    const containerDiv = {

        "display":"flex"

    }

    function ChangeValueAndFocus(index, e){

        inputsText[index] += e.key;

        inputsRef[(index + 1) % inputCount].current.focus();

        setInputsText([...inputsText]);

    }

    return (

        <div style={containerDiv}>

            {

                inputsText.map((item, index) => (

                    <input  

                        value={item}

                        key={index} 

                        autoFocus = {index === 0 ? true : false}

                        tabIndex={index}

                        ref={inputsRef[index]}                   

                        onKeyPress={(e) => ChangeValueAndFocus(index, e)} />                  

                ))

            }

        </div>

    )

}

הי

את יכולה לראות אם הפונקציה
ChangeValueAndFocus
מופעלת?

(למשל להוסיף בתוכה הדפסה או נקודת עצירה)

ואם מופעלת, לנסות להבין האם הערך של inputRefs ו index הוא נכון? האם הם מצביעים לאלמנט המתאים?

כן, עשיתי שם קונסול וזה מדפיס אותו, ממש מוזר

את מדברת על הפוקוס הראשוני או על שינוי פוקוס אחרי לחיצה? כי עכשיו ניסיתי אצלי ואני רואה שהוא כן מחליף פוקוס בלחיצה. הנה כאן:

וגם הפוקוס הראשון מגיע לאלמנט הנכון

אני לא יודעת, ניסיתי ואצלי זה לא עובד לתיבה הבאה
image

אה כן עכשיו אני רואה! השתמשת ב onKeyPress במקום ב onChange

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

כלל אצבע בריאקט הוא: או שמשתמשים ב value יחד עם onChange, או באף אחד משניהם. אף פעם לא רק אחד מהם בלי השני

תודה רבה, זה עובד ב"ה וממש נחמד איך שעשית במקום למלא מערך שלם בUSEREF לכל האינפוטים פשוט עשית REF אחד לדיב העוטף, ממש אהבתי…
בקשר לתרגיל 2 ככה עשיתי עם הSTATE אשמח לreview של הקוד וכן רציתי לשאול, למה אם הסיסמאות לא שוות ונעשה ריענון של הטופס, הסיסמאות נעלמות וצריך להכניס מחדש? בדרכ בטפסים אמיתיים זה נשאר ואז מתקנים

import React, { useState } from 'react';

export default function Targil2B(){

    const [pas1, setPas1] = useState("");    

    const [pas2, setPas2] = useState("");    

    function validate(){

        pas1 == pas2 ? alert("You have successfully registered") : alert("Password and verify password are not equal");

    }

    return (

        <form>

            <div>

                <label>

                    Name:

                    <input type="text" name="userName" />

                </label>

            </div>

            <div>

                <label>

                    Password:

                    <input type="password" name="password" value={pas1} onChange={(e) => setPas1(e.target.value)}/>

                </label>

            </div>

            <div>

                <label>

                    Verify password:

                    <input type="password" name="verifyPassword" value={pas2} onChange={(e) => setPas2(e.target.value)}/>

                </label>

            </div>

            <button type="submit" onClick={validate}>Sign</button>

        </form>

    )

}