קורס React 2020 שיעור תרגול עבודה עם רשימות

אין שגיאה
זה משהו מוזר אני לוחצת על הכותרת של אחת העמודות ולא קורה כלום, לוחצת פעם שניה ונעלמות 2 שורות לוחצת פעם שלישית והכל נעלם
מה יכול להיות?

לא הרצתי או דיבגתי אז הכל ניחושים, אבל הנה מה שאני הייתי עושה:

בעיה ראשונה- אני רואה את הפקודה:

data.shift()

שהיא מאוד מטרידה. הפקודה משנה משהו ב data (מוחקת תא מהמערך) ואת מריצה אותה כל פעם שמפעילים את פונקציית הרכיב. זכרי שפונקציית הרכיב אמורה להיות פונקציה טהורה ללא Side Effects, ושינוי מידע בזיכרון זה Side Effect.

בעיה שניה באותו סגנון היא הקריאה ל sort ישירות על data. הפקודה sort משנה את המערך - ושוב זהו Side Effect. ריאקט לא מצפה שזה יקרה. הקריאה ל sortTable צריכה קודם כל להעתיק את data הצידה ואז להפעיל sort על העותק, או להשתמש בספריה שעושה את זה בשבילך כמו:
https://lodash.com/docs/4.17.15#sortBy

אחרי שאת מוחקת את כל ה Side Effects יהיה לנו הרבה יותר קל לדבג: בכלי הפיתוח של הדפדפן אחרי התקנת התוסף React DevTools תוכלי להסתכל על הערך של כל משתני הסטייט והפרופס ולעקוב אחרי החישובים כדי לראות שמקבלים את התוצאה שצריך

שיניתי מה שאמרת ועדיין זה עושה אותו דבר
אני מנסה להדפיס בקונסול את הערך של דטה וכבר בתחילת התרגיל הוא מדפיס לי אותו בלי השורה הראשונה
import React, { useState } from ‘react’;

export default function SortableTable(props){

const { data } = props;

console.log("first",data);

const [ columnToSort, setColumnToSort ] = useState(0);

const dataToShow = sortTable( data, columnToSort);

function sortTable(data, columnToSort){

    const d = data;

    d.shift();

    const order = d[0][columnToSort] > d[1][columnToSort];

    d.sort(function( a, b ){

        const firstValue = a[columnToSort];

        const secondValue = b[columnToSort];

        if ( firstValue < secondValue )

            if( !order ) return 1; else return -1;

        return 0;

    });

    return d;

}

return (

    <>

        <table>

            <thead>

                {

                    data[0].map((item, index) => (

                        <th onClick={ () => setColumnToSort(index)  }>{item}</th>

                    ))

                }

            </thead>

            <tbody>

                {

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

                        <tr key={index}>

                            {

                                item.map((item2, index2) => (

                                    <td>{item2}</td>

                                ))

                            }

                        </tr>

                    )

                    )

                }

            </tbody>

        </table>

    </>

)

}

אני רואה שפקודת ה shift עדיין נשארה בתוך הקומפוננטה

אבל אני לא מבינה מה הבעיה שהיא תהיה?
היא בתוך הפונקציה והיא מופעלת רק בלחיצה על אחת הכותרות ורק על ההעתק של data

היא לא מופעלת על העתק של data אלא על data עצמו. איפה ראית פעולת העתקה?

אני מעתיקה את data לתוך d ועל d מפעילה את shift
image

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

const d = data;

כל פעולה שתעשי על d תשפיע על data ולהיפך.

נסי את זה:

const arr = [10, 20, 30];
const brr = arr;
brr.push(50);

console.log(arr);
console.log(brr);

כאן יש הסבר טוב לתופעה עם רעיונות לפיתרונות ושווה לקרוא:

תודה!! ממש לא שמתי לב,
הגעתי לפיתרון מסויים שעובד, אשמח לשמוע איך אפשר לשפר אותו וגם, לגבי שורה 5

    const [ dataToShow, setDataToShow ] = useState([data[1],data[2],data[3],data[4]]);

אני יודעת שהיא לא דינאמית בעליל אבל ממש לא הצלחתי להגיע לשום פיתרון שיתן לי בבת אחת את כל המערך בלי השורה הראשונה, ניסיתי shift וslice אבל זה מביא לי את התא הראשון ולא את שאר המערך… אשמח לפיתרון יותר יעיל

import React, { useState } from 'react';

export default function SortableTable(props){

    const { data } = props;

    const [ dataToShow, setDataToShow ] = useState([data[1],data[2],data[3],data[4]]);

    function sortTable(dataForSort, columnToSort){

        const order = dataForSort[0][columnToSort] > dataForSort[1][columnToSort];

        dataForSort.sort(function( a, b ){

            const firstValue = a[columnToSort];

            const secondValue = b[columnToSort];

            if ( firstValue < secondValue )

                if( !order ) return 1; else return -1;

            if ( firstValue > secondValue )

                if( order ) return 1; else return -1;

            return 0;

        });

        setDataToShow([...dataForSort]);

    }

    return (

        <>

            <table>

                <thead>

                    {

                        data[0].map((item, index) => (

                            <th onClick={() => sortTable(dataToShow, index)}>{item}</th>

                        ))

                    }

                </thead>

                <tbody>

                    {

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

                            <tr key={index}>

                                {

                                    item.map((item2, index2) => (

                                        <td>{item2}</td>

                                    ))

                                }

                            </tr>

                            

                        )

                        )

                    }

                </tbody>

            </table>

        </>

    )

}

הי,

לגבי חיתוך כל המערך מלבד התא הראשון נסי את זה:

data = [10, 20, 30, 40];
withoutFirst = data.slice(1);
console.log(withoutFirst);

אבל יותר מעניין - למה את צריכה לשמור את dataToShow בתור משתנה State ?

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

אשמח לשמוע איך אתה היית עושה את זה?

מה הכוונה “לא מתמיין בפעם השניה?” מה את רוצה שיקרה בלחיצה שניה?

אני רוצה שבלחיצה שניה יתהפך סדר המיון - מעולה ליורד או להפך

במצב כזה נשמע שיש לך שני משתנים שצריכים להישמר בסטייט: מספר העמודה וסדר המיון.

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

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

import React, { useState } from 'react';

export default function SortableTable(props){

    const { data } = props;

    const [sortedColumn, setSortedColumn] = useState(0);

    const [sortOrder, setSortOrder] = useState(0);

    const dataToShow = data.slice(1);

    function sortTable(columnToSort){

        if(columnToSort == sortedColumn) setSortOrder(!sortOrder);

        else {

            setSortedColumn(columnToSort);

            setSortOrder(dataToShow[0][columnToSort] > dataToShow[1][columnToSort]);

        }

        dataToShow.sort(function( a, b ){

            const firstValue = a[columnToSort];

            const secondValue = b[columnToSort];

            if ( firstValue < secondValue )

                if( !sortOrder ) return 1; else return -1;

            if ( firstValue > secondValue )

                if( sortOrder ) return 1; else return -1;

            return 0;

        });

    }

    return (

        <>

            <table>

                <thead>

                    {

                        data[0].map((item, index) => (

                            <th onClick={() => sortTable(index)}>{item}</th>

                        ))

                    }

                </thead>

                <tbody>

                    {

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

                            <tr key={index}>

                                {

                                    item.map((item2, index2) => (

                                        <td>{item2}</td>

                                    ))

                                }

                            </tr>

                            

                        )

                        )

                    }

                </tbody>

            </table>

        </>

    )

}

הי @ss1

הקוד עדיין לא בנוי טוב ולכן לא עובד:

  1. יש לנו את הפונקציה הראשית SortableTable ובתוכה את הפונקציה הפנימית sortTable

  2. הפונקציה הפנימית צריכה רק לשנות את משתני ה State, כלומר יש למחוק את כל מה שאחרי בלוק ה else.

  3. הפונקציה הראשית SortableTable צריכה למיין את המידע אבל בלי לשנות אותו, כלומר sort לבד לא מספיק טובה (כי היא משנה את המערך במקום). יש כאן דיון עם רעיונות טובים איך למיין מערך בלי לקלקל אותו:
    https://stackoverflow.com/questions/30431304/functional-non-destructive-array-sort

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

מגניב!!
עובד מושלם, רק הייתי צריכה להזיז את הקוד שאחרי הelse ללמעלה
import React, { useState } from ‘react’;

export default function SortableTable(props){

const { data } = props;

const [sortedColumn, setSortedColumn] = useState(0);

const [sortOrder, setSortOrder] = useState(1);

const dataToShow = data.slice(1);

dataToShow.sort(function( a, b ){

    const firstValue = a[sortedColumn];

    const secondValue = b[sortedColumn];

    if ( firstValue < secondValue )

        if( !sortOrder ) return 1; else return -1;

    if ( firstValue > secondValue )

        if( sortOrder ) return 1; else return -1;

    return 0;

});

function sortTable(columnToSort){

    if(columnToSort == sortedColumn) setSortOrder(!sortOrder);

    else {

        setSortedColumn(columnToSort);

        setSortOrder(dataToShow[0][columnToSort] > dataToShow[1][columnToSort]);

    }

}

return (

    <>

        <table>

            <thead>

                {

                    data[0].map((item, index) => (

                        <th onClick={() => sortTable(index)}>{item}</th>

                    ))

                }

            </thead>

            <tbody>

                {

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

                        <tr key={index}>

                            {

                                item.map((item2, index2) => (

                                    <td>{item2}</td>

                                ))

                            }

                        </tr>

                        

                    )

                    )

                }

            </tbody>

        </table>

    </>

)

}

יש עוד משהו לשפר?

לייק 1

נראה מעולה ושווה לקחת מכאן כלל-

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

מצרף פיתרון שלי , אשמח להערות על הקוד
    const SortableTable = (props) => {

      const { data } = props;

      const [tableHeader, setTableHeader] = useState(data[0]);

      const [table, setTable] = useState(data.slice(1));

      const [count, setCount] = useState(0);

      const [lastChoise, setLastChoise] = useState(0);

      function sortArray(index) {

        let table = [...data.slice(1)];

        if (count % 2 == 0)

          table = table.sort((a, b) => (b[index] > a[index] ? 1 : -1));

        else

          table = table.sort((a, b) => (b[index] < a[index] ? 1 : -1));

        setTable(table);

        if(lastChoise == index)

          setCount(c => c + 1)

        setLastChoise(index);

      }

      return (

        <div>

          <h1 id='title'>React Dynamic Table</h1>

          <table>

            <thead>

              <tr>

                {tableHeader.map((item, j) => (<th key={j} onClick={() => sortArray(j)}>{item}</th>))}

              </tr>

            </thead>

            <tbody>

              {table.map((item, index) => (

                <tr key={index}>

                  {item.map((details, j) => (<td key={j}>{details}</td>))}

                </tr>))}

            </tbody>

          </table>

        </div>

      );

    }

הי

נתחיל עם הפיל שבחדר - משתנה הסטייט table הוא מיותר. או לפחות הוא לא באמת משתנה State. זה פשוט עותק ממוין של המידע שקיבלת מבחוץ, ולכן יותר מתאים להתיחס אליו בתור Computed Value.

אנחנו נוהגים לחשב ערכים מחושבים (Computed Values) בריאקט בכניסה לפונקציית הקומפוננטה ולא לשמור אותם בסטייט.

אז בקשה ראשונה - תוציא את table מהסטייט ובמקומו שמור בסטייט רק את העמודה שלפיה צריך למיין. בכניסה לפונקציית הקומפוננטה תמיין את המידע שקיבלת לפי אותה עמודה. הפונקציה sortTable לא צריכה לעשות את כל המיון אלא רק לשנות את העמודה שלפיה ממיינים (או את סדר המיון בעמודה)