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

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

new Array(10).fill(null).map((_, index) => index === 4 ? 'Winner' : 'Normal Square')

הרי כל פונקציה שאני מגדירה יוצרת משתנה בזיכרון. וכש- state משתנה הוא יוצר את הפונקציות שבתוך הפקד
כלומר מגדיר מחדש את המשתנים שלהם בזיכרון.
לדוג’ בפקד הבא , כאשר לוחצים על הכפתור הפקד מתרנדר מחדש, האם הפונקציה handleClick גם נוצרת מחדש והקודמת שכבר לא בשימוש נשארת בזכרון??

export default function app() {
    const [ num , setNum ] = useState(null);


    function handleClick() {
      setNum( v => v + 1);
    }

    return(
        <>
            <p>{num}</p>
            <button onClick={handleClick}></button>
        </>
    );
}

הי,

הפונקציה handleClick נוצרת מחדש, אבל הפונקציה handleClick “הקודמת” (זו שנוצרה בהפעלה הקודמת של פונקציית הפקד) כבר נמחקה מהזיכרון כי אף אחד לא מסתכל עליה. במובן זה אין פה בזבוז או זליגה של זיכרון.

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

היי,
תשובה לשאלה 2 אשמח לשמוע מה לשפר.

import React, { useState } from 'react';

const ItemList = (props) => {

    const { listOfItemsAfterSort } = props;

    return (

        <ul>

            {listOfItemsAfterSort.map((item,indx) => (

                <li>{item}</li>

            ))}

        </ul>

    )

}

const SearchBox = (props) => {

    const { handleTextChange } = props;

    return(

        <input type='text' onChange={ (e) => handleTextChange(e.target.value)}></input>

    )

}

export default function targil2() {

    const listOfItems = ['blue', 'green', 'pink', 'gray', 'yellow','black','red','white','purple','silver','gold'];

    const [listOfItemsAfterSort, setListOfItemsAfterSort] = useState(['blue', 'green', 'pink', 'gray', 'yellow','black','red','white','purple','silver','gold']);

    function handleTextChange(text)

    {

        const neArr = [];

        listOfItems.forEach(item => item.includes(text) && neArr.push(item));

        setListOfItemsAfterSort(neArr);

    }

return (

<>

    <SearchBox handleTextChange={handleTextChange}></SearchBox>

    <ItemList listOfItemsAfterSort={listOfItemsAfterSort}></ItemList>

</>)

}

הי @ss1

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

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

מקווה שהבנתי, ככה?

import React, { useState } from 'react';

const ItemList = (props) => {

    const listOfItems = ['blue', 'green', 'pink', 'gray', 'yellow','black','red','white','purple','silver','gold'];

    const { TextForSearch } = props;

    return (

        <ul>

            {listOfItems.map((item,indx) => (

            item.includes(TextForSearch) && <li>{item}</li>

            ))}

        </ul>

    )

}

const SearchBox = (props) => {

    const { handleTextChange } = props;

    return(

        <input type='text' onChange={ (e) => handleTextChange(e.target.value)}></input>

    )

}

export default function targil2() {

    const [TextForSearch, setTextForSearch] = useState('');

    function handleTextChange(text)

    {

        setTextForSearch(text);

    }

return (

<>

    <SearchBox handleTextChange={handleTextChange}></SearchBox>

    <ItemList TextForSearch={TextForSearch}></ItemList>

</>)

}

מעולה רק שבדרך כלל נשתמש באותיות קטנות בלבד לשמות משתנים, ונשתמש ב filter לפני map משהו כזה:

items.filter(item => item.includes(searchText)).map((item) => <li>{item}</li>)

היי,
ניסיתי את תרגיל 3 אבל אני מקבלת שגיאה
Too many re-renders. React limits the number of renders to prevent an infinite loop

אני לא מצליחה להבין מה הבעיה

import React, { useState } from 'react';

const Page1 = (props) => {

    const { handleNameChange,  handlePasswordChange} = props;

    return (

        <>

            <label>Username:</label>

            <input type="text" onChange={(e) => handleNameChange(e.target.value)}></input>

            <label>Password:</label>

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

        </>

    )

}

const Page2 = (props) => {

    const { handleCountryChange, handleCityChange } = props;

    return (

        <>

            <label>Country:</label>

            <input type="text" onChange={(e) => handleCountryChange(e.target.value)}></input>

            <label>City:</label>

            <input type="text" onChange={(e) => handleCityChange(e.target.value)}></input>            

        </>

    )

}

const Page3 = (props) => {

    const { name, password, country, city } = props;

    return(

    <p>Summery of your details: 

        name : {name},

        password: {password},

        country: {country},

        city: {city}

    </p>

    )

} 

export default function Targil3()

{

    const [name, setName] = useState('');

    const [password, setPassword] = useState('');

    const [country, setCountry] = useState('');

    const [city, setCity] = useState('');

    const [currentPage, setCurrentPage] = useState(1);

    function handleNameChange(text){

        setName(text);

    }

    function handlePasswordChange(text){

        setPassword(text);

    }

    function handleCountryChange(text){

        setCountry(text);

    }

    function handleCityChange(text){

        setCity(text);

    }

    return (

        <>

        <button onClick={setCurrentPage(() => currentPage === 1 ? 1 : currentPage-1)}>Previous</button>

        <button onClick={setCurrentPage(() => currentPage === 3 ? 3 : currentPage+1)}>Next</button>

        {currentPage === 1 && <Page1 handleNameChange = {handleNameChange} 

        handlePasswordChange = {handlePasswordChange}></Page1>}

        {currentPage === 2 && <Page2 handleCountryChange = {handleCountryChange}

        handleCityChange = {handleCityChange}></Page2>}

        {currentPage === 3 &&<Page3 name = {name} password = {password} country = {country} city = {city}></Page3>}

        </>

    )

}

הי @ss1

שימי לב לשורה:

        <button onClick={setCurrentPage(() => currentPage === 1 ? 1 : currentPage-1)}>Previous</button>

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

עדיף לכתוב פונקציה שמעבירה לעמוד הבא ולהעביר את הפונקציה הזו ל onClick של הכפתור

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

import React, { useState } from ‘react’;

const Page1 = (props) => {

const { handleNameChange,  handlePasswordChange} = props;

return (

    <>

        <label>Username:</label>

        <input type="text" onChange={(e) => handleNameChange(e.target.value)}></input>

        <label>Password:</label>

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

    </>

)

}

const Page2 = (props) => {

const { handleCountryChange, handleCityChange } = props;

return (

    <>

        <label>Country:</label>

        <input type="text" onChange={(e) => handleCountryChange(e.target.value)}></input>

        <label>City:</label>

        <input type="text" onChange={(e) => handleCityChange(e.target.value)}></input>            

    </>

)

}

const Page3 = (props) => {

const { name, password, country, city } = props;

return(

<p>Summery of your details: 

    name : {name},

    password: {password},

    country: {country},

    city: {city}

</p>

)

}

export default function Targil3()

{

const [name, setName] = useState('');

const [password, setPassword] = useState('');

const [country, setCountry] = useState('');

const [city, setCity] = useState('');

const [currentPage, setCurrentPage] = useState(1);

function handleNameChange(text){

    setName(text);

}

function handlePasswordChange(text){

    setPassword(text);

}

function handleCountryChange(text){

    setCountry(text);

}

function handleCityChange(text){

    setCity(text);

}

return (

    <>

    <button onClick={() => setCurrentPage(currentPage === 1 ? 1 : currentPage-1)}>Previous</button>

    <button onClick={() => setCurrentPage(currentPage === 3 ? 3 : currentPage+1)}>Next</button>

    {currentPage === 1 && <Page1 handleNameChange = {handleNameChange} 

    handlePasswordChange = {handlePasswordChange}></Page1>}

    {currentPage === 2 && <Page2 handleCountryChange = {handleCountryChange}

    handleCityChange = {handleCityChange}></Page2>}

    {currentPage === 3 &&<Page3 name = {name} password = {password} country = {country} city = {city}></Page3>}

    </>

)

}

היי ינון!
קודם כל תודה רבה על כל השיעורים הכ"כ מובנים!
לגבי התרגיל הראשון,
איך אני בעצם יוצרת 10 רבועים?
עצבתי את הdiv שלי ככה

<div style={{

      background: '#d2d2d2',

      width: '150px'

      , height: '150px',

      boxShadow: '0 0 1px 1px rgba(0,0,0,0.6)',

    }}>

איך אני בעצם מעצבת את זה על 10 פקדים?
אם תוכל לתת לי הכוונה פחות או יותר איך לגשת לתרגיל הזה,אני ממש אשמח…
תודה רבה מראש!

הי,

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

const style = { background: '#d2d2d2', width: '150px', height: '150px' };

return (
  <div style={style} />
);

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

לייק 1

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

import React, { useState } from 'react';

const App = () => {

  const style = { background: '#d2d2d2', width: '150px', height: '150px' };

  const [index, setIndex] = useState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

  const random = Math.random() * 10;

  function changePlace() {

    setIndex(random);

  }

  return (

    <div>

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={style} index={index} /><br />

      <div style={{ background: 'red', width: '150px', height: '150px' }} index={index} onClick={changePlace} /><br />

    </div>

  );

}

export default App;

הי,

לגבי המיקום אם תגדירי בסטייל:

display: 'inline-block'

אז הם יופיעו אחד ליד השני. אפשר ללמוד עוד על נושא סידור בפרק CSS בקורס פרונט אנד כאן:
https://www.tocode.co.il/bundles/frontend/lessons/box-model
(ובשיעורים שאחריו)

עכשיו לגבי הקוד לא הבנתי מה ניסית לעשות בשורה:

  const [index, setIndex] = useState(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

או באופן כללי את הלוגיקה של המשחק


אני הייתי ניגש לזה אחרת:

  1. הייתי מגריל מספר אקראי ושומר אותו בתוך משתנה ב State - זה יהיה האינדקס של הריבוע האדום

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

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

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

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

בשביל לשמור מערך של אינדקסים ב State צריך לעשות משהו קצת יותר מורכב ואני מדבר על זה בשיעור הזה:
https://www.tocode.co.il/bundles/react/lessons/state

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

במשחק יותר מתוחכם (למשל משחק זיכרון) כן צריך לשמור מערך עם כל הנתונים אבל לזה נגיע בהמשך הקורס

לייק 1

היי אשמח לחוות דעת על הפיתרון של תרגיל 1 תודה :slight_smile:

Home Component

import { useState } from "react";
import Score from "./Score";
import Squres from "./Squres";

export default function Home() {
  const [score, setScore] = useState(0);

  const changeScore = (color) => {
    if (color === "grey") setScore((prev) => prev - 5);
    else setScore((prev) => prev + 10);
  };
  const resetScore = () => {
    setScore(0);
  };
  return (
    <div>
      <Score score={score} resetScore={resetScore} />
      <Squres changeScore={changeScore} />
    </div>
  );
}

Squres Component

export default function Squres({ changeScore }) {
  const squreStyle = {
    width: "50px",
    height: "50px",
    backgroundColor: "grey"
  };
  const squreRedStyle = {
    width: "50px",
    height: "50px",
    backgroundColor: "red"
  };
  const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  const random = Math.floor(Math.random() * 10);

  return (
    <div
      style={{
        display: "flex",
        justifyContent: "space-around",
        flexDirection: "row"
      }}
    >
      {array.map((e, index) => {
        return random === index ? (
          <div
            onClick={(e) => changeScore(e.target.style.backgroundColor)}
            style={squreRedStyle}
          ></div>
        ) : (
          <div
            onClick={(e) => changeScore(e.target.style.backgroundColor)}
            style={squreStyle}
          ></div>
        );
      })}
    </div>
  );
}

Score Component

export default function Score({ score, resetScore }) {
  return (
    <div style={{ margin: "2% 0 5%" }}>
      <h3>Score: {score}</h3>
      <button onClick={resetScore}>Reset Score</button>
    </div>
  );
}

הי

נראה מעולה ועושה רושם שעובד. אהבתי שהשתמשת ב setState בשתי הצורות שלו - גם setScore(0) כשזה ערך קבוע וגם עם פונקציה כשזה ערך שתלוי בערך הקודם.

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

נסה להוסיף בתוך הקומפוננטה Squares צבע רקע בהיר ל div הראשי, וכפתור שנקרא Dark Mode שהופך את צבע הרקע לכהה. ספר אם התוספת הזאת שברה לך משהו במשחק ולמה זה קרה.

בתרגיל 1 כאשר אני מוסיפה את הפונקציה לחישוב הנקודות (updateScore) מופיעה השגיאה: Uncaught TypeError: Assignment to constant variable.
אשמח אם תוכל להסביר לי מדוע

export default function Sq(props){

    const {isRed,func} = props;

    const backgroundColor = isRed ? 'red' : 'gray';

    

    let style={

        width:"100px",

        height:"100px",

        backgroundColor: backgroundColor,

        margin:"10px",

        display:"inline-block"

    }

    return(

        <>

         <div style = {style} onClick = {func}></div>   

        

        </>

    )

}
export default function Boxes(){

    const [square,setSquare] = useState([false,false,false,false,true])

    const [score,setScore] = useState(0);

    function update(inx){

        setInterval(

            function(){

                let newArr = [false,false,false,false,false];

                newArr[Math.floor(Math.random() * 5)] = true;

                setSquare(newArr);

            },1000

        )

    }

    function updateScore(inx2) {

        setScore(square[inx2]?score+=10:score-=5)

    }

    return(

        <>

            <Sq func = {updateScore(0)} isRed = {square[0]}/>

            <Sq func = {updateScore(1)} isRed = {square[1]}/>

            <Sq func = {updateScore(2)} isRed = {square[2]}/>

            <Sq func = {updateScore(3)} isRed = {square[3]}/>

            <Sq func = {updateScore(4)} isRed = {square[4]}/>


            <div>

                <button onClick = {update}>new game</button>

                <p>{score}</p>

            </div>

        </>

    )

}

הי
משהו לא נכון בשימוש שלך באופרטור סימן-שאלה-נקודותיים. אופרטור זה נועד להחזיר ערך ולא לביצוע פעולה. בנוסף אין משמעות לשינוי המשתנה score עצמו. צריך לחשוב עליו בתור “מצביע” למקום בזיכרון בו ריאקט שומר את הערך האמיתי.

הכתיב הנכון יהיה:

function updateScore(inx2) {
  const diff = square[inx2] ? 10 : -5;
  setScore(currentScore => currentScore + diff);
}