קורס React 2020 שיעור שתי תיבות בחירה מתואמות לבחירת מדינה ועיר


זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/b5892b02-c3b5-4eb8-badd-9e6629ee91d7

שלום,
יש לי כמה שאלות אם אפשר:

1.) לא הבנתי מדוע הפקד SelectCountry צריך לקבל את selectedCountry ולאתחל את ה-value של ה-select באותו ערך, מה המשמעות של זה? הרי כשמשנים את הבחירה השינוי הזה נעשה אוטומטי.
אותו דבר לגבי הפקד - SelectCity.
מצרפת את הקוד עם 2 השורות מסומנות:

const SelectCountry = (props) => {
  const { selectedCountry, setSelectedCountry, countries } = props;

  return (
    //בשורה הבאה:
  <select value={selectedCountry} onChange={(e) => setSelectedCountry(e.target.value)}>
    <option disabled selected value>Please select a country</option>
    {countries.map((country, index) => (
      <option key={index} value={country}>{country}</option>
    ))}
  </select>
  );
};

const SelectCity = (props) => {
  const { selectedCity, setSelectedCity, cities } = props;

  return (
  //בשורה הבאה:
  <select value={selectedCity} onChange={(e) => setSelectedCity(e.target.value)}>
    <option disabled selected value>Please select a city</option>
    {cities.map((city, index) => (
      <option key={index} value={city}>{city}</option>
    ))}
  </select>
  );
};

2.) דבר דומה היה בתרגיל של בחירת יום בשבוע, מדוע צריך ב - checkbox לאתחל את ה-checked:
checked={selectedItems.has(item)}

import React from 'react';
import ReactDOM from 'react-dom';
import { useState } from 'react';

function SelectableList(props) {
  const { items } = props;
  const [selectedItems, setSelectedItems] = useState(new Set());  

  function toggleItem(add, item) {    
    if (add) {
      selectedItems.add(item);
    } else {
      selectedItems.delete(item);
    }

    console.log(selectedItems);

    setSelectedItems(new Set(selectedItems));
  }

  return (
    <>
      <p>Selected Items: {Array.from(selectedItems).join(', ')}</p>
      <ul>
        {items.map(item => (
          <li key={item} style={{ direction: "rtl" }}>
            <label>
              {item}
              <input
              type="checkbox"
              checked={selectedItems.has(item)}
              onChange={(e) => toggleItem(e.target.checked, item)}
              />
            </label>
          </li>
        ))}
      </ul>
    </>
  );
}


const App = () => {
  const days = ['Sunday', 'Monday', 'Tuesday','Wednesday', 'Thursday', 'Friday', 'Saturday'];
  return (
    <div>
      <SelectableList items={days}/>
    </div>
  )
};


// main.js
const root = document.querySelector('main');
ReactDOM.render(<App />, root);

3.) שאלה כללית על ה- useState, אמרת באחד השיעורים שהפונקציה הזו מקבלת פונקצית עדכון או ערך יחיד, ובפונקצית העדכון משתמשים כאשר הערך החדש תלוי בישן. לא הבנתי מדוע צריך את הפונקציה הזו אם אפשר להשתמש במשתנה שהגיע מה-state לדוגמא בקוד הבא:

export default function Counter(props) {
  const [count, setCount] = useState(0);
  const [delta, setDelta] = useState(1);

  function inc() {
    setCount(oldValue => oldValue + delta);
  }

  return (
  );
}

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


  function inc() {
    setCount(count + delta);
  }

תודה רבה!!!

הי

בואי ננסה להתחיל מהסוף - לגבי ה state. מה תהיה התוצאה של הרצת שתי השורות הבאות ברצף?

setValue(val + 1);
setValue(val + 1);

התשובה שהערך בסטייט יעלה ב-1 (ולא ב-2), כי הפעולה של setValue לא משפיעה על המשתנה val והערך שכבר נמצא בתוכו.

לעומת זאת בפונקציות אין לנו בעיה כזאת והקוד הבא:

setValue(v => v + 1);
setValue(v => v + 1);

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

לגבי הטפסים וקומפוננטת ה Select:

בכל קומפוננטת טופס בריאקט אנחנו יכולים לבחור אם להעביר את המאפיינים value ו onChange או לוותר עליהם. כשמעבירים אותם הקומפוננטה נקראת “קומפוננטה מנוהלת” וריאקט דואג לסנכרן בין הערכים של המשתנים לשני הכיוונים, כלומר כשהערך משתנה בקומפוננטה גם ערכי המשתנים יעודכנו. במקרה שלנו המשתנה הוא selectedCountry והערך שלו יהיה מסונכרן עם מה שיש בקומפוננטה.

אפשרות שניה היא לא להעביר את value ואת onChange. במצב כזה עדיין תיבת ה select תעבוד אבל לא יהיה סינכרון של המידע בחזרה למשתנים שלנו ולכן יהיה קשה יותר להשתמש במידע “איזה ארץ ועיר נבחרו” במקומות אחרים בתוכנית.

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

לייק 1

1.) בקשר ל-state.
באופציה השניה:

 setValue(v => v + 1);
 setValue(v => v + 1);

מאיפה לוקח ריאקט את הפרמטר ‘v’? ממה ששמור כרגע בסטייט? ז"א הערך בסטייט מתעדכן לאחר ביצוע כל שורה?

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

2.) לגבי קומפוננטת ה-select התכוונתי לאפשרות שלישית שהיא לטפל רק באירוע של onChange ולא לתת ערך ב-checked, ז"א רק להוריד מהתוכנית את השורה הזו:

              checked={selectedItems.has(item)}

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

היי ינון

איך אפשר לקבל את ה-index (ולא את ה-value) מתיבת select box?

אפשר להשתמש במאפיין selectedIndex של אירוע שינוי בתיבת select. הנה דוגמת קוד:

היי ינון
כתבתי את אותו הקוד שאתה כתבת והוא עובד כמו שצריך אבל הוא זורק לי את כמה שגיאות :
1 . Warning: Each child in a list should have a unique “key” prop.
2 . Use the defaultValue or value props on instead of setting selected on .
3 . Warning: value prop on select should not be null. Consider using an empty string to clear the component or undefined for uncontrolled
4 . Unchecked runtime.lastError: The message port closed before a response was received.

זה הקוד

import { useState } from "react";

function SelectCountry(props){
const {countries,country,replaceCountry} = props;
return(
    <>
        <select value={country} onChange={(e)=>replaceCountry(e.target.value)}>
            <option disabled selected value>Select Country</option>
            {countries.map((item,index)=>(
                <option value={item}>{item}</option>
            ))}
        </select>
    </>
);
}

function SelectCity(props){
const {cities,city,setCity} = props;    
return(
    <>
        <select value={city} onChange={(e)=>setCity(e.target.value)}>
            <option disabled selected value>Select City</option>
            {cities.map((item,index)=>(
                <option value={item}>{item}</option>
            ))}
        </select>
    </>
);
}
function SelectCountryAndCity(props){
 const {countriesAndCities} = props;
const [country,setCountry] = useState(null);
const [city,setCity] = useState(null);

const countries = Object.keys(countriesAndCities);
const cities = countriesAndCities[country];

const replaceCountry=(newCountry)=>{
    setCountry(newCountry);
    setCity(null);
}
return(
    <div>
        <p>You Live {country} / {city}</p>
        <SelectCountry
            countries={countries}
            country={country}
            replaceCountry={replaceCountry}
            />
        {cities && <SelectCity
            cities={cities}
            city={city}
            setCity={setCity}
        />}
    </div>
);
}

export default SelectCountryAndCity

הי אייל

תודה על תשומת הלב. אכן בריאקט מומלץ לא להשתמש ב null בתור ערך של select ועדיף להשתמש במחרוזת ריקה. זאת התוכנית המתוקנת:

import { useState } from 'react'

function SelectCountry(props) {
  const { countries, country = "", replaceCountry } = props;
  return (
    <>
      <select value={country} onChange={(e) => replaceCountry(e.target.value)} >
        <option disabled value>
          Select Country
        </option>
        {countries.map((item, index) => (
          <option value={item}>{item}</option>
        ))}
      </select>
    </>
  );
}

function SelectCity(props) {
  const { cities, city = "", setCity } = props;
  return (
    <>
      <select value={city} onChange={(e) => setCity(e.target.value)} >
        <option disabled value={undefined}>
          Select City
        </option>
        {cities.map((item, index) => (
          <option value={item}>{item}</option>
        ))}
      </select>
    </>
  );
}

function SelectCountryAndCity(props) {
  const { countriesAndCities } = props;
  const [country, setCountry] = useState("");
  const [city, setCity] = useState("");

  const countries = Object.keys(countriesAndCities);
  const cities = countriesAndCities[country];

  const replaceCountry = (newCountry) => {
    setCountry(newCountry);
    setCity("");
  };
  return (
    <div>
      <p>
        You Live {country} / {city}
      </p>
      <SelectCountry
        countries={countries}
        country={country}
        replaceCountry={replaceCountry}
      />
      {cities && <SelectCity cities={cities} city={city} setCity={setCity} />}
    </div>
  );
}

לגבי ההערה -

1 . Warning: Each child in a list should have a unique “key” prop.

היא מאוד חשובה ואני משאיר אותה כאן בכוונה. יש עליה שיעור בהמשך הקורס:
https://www.tocode.co.il/bundles/react/lessons/key?tab=video