קורס React 2020 שיעור טעינת מידע ב Ajax באמצעות useEffect


זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/ajax-useeffect

פתרון לתרגיל מהסרטון:

שתי שאלות אם אפשר:
1.) האם אפשר להוסיף תנאי להפעלה של useEffect? במקום להוסיף בתוך הפונקציה את השורה הבאה:

 if(!dataCharacterDescription) return;

2.) הפתרון שלי מציג את ה-films אחד אחד.
אם אני רוצה לחכות שכולם יחזרו ורק אחר כך להציג אותם , יש אפשרות לעשות זאת מבלי להוסיף משתנה סטייט נוסף שיתריע על הdata האחרונה? ניסיתי לעשות את זה עם Promise.all כך:

  useEffect(()=>{
        if(!dataCharacterDescription) return;
        const filmsUrls = dataCharacterDescription.films;
        const xhrFilms = filmsUrls.map(filmsUrl=>$.getJSON(filmsUrl));
        Promise.all(xhrFilms).then(setDataFilms);
        return(()=>xhrFilms.forEach(xhrFilm => xhrFilm.abort()));
    },[dataCharacterDescription]);

הבעיה היא שכאשר יש ביטול של הבקשות הוא מתריע על שגיאה בשורה של ה-Promise - בגלל שהבקשות בוטלו.

ממש תודה על כל העזרה!



main.js

import ReactDOM from 'react-dom';
import StarwarsCharacter from './starwarsCharacter';

const App = () => {
  const [id , setId] = useState(1);

  return (
    <div>
        <input type ="number" value={id} onChange={(e)=>setId(e.target.value)}/>
        <StarwarsCharacter id = {id}/>
    </div>
  );
}

ReactDOM.render(<App/> , document.querySelector('main'));

starwarsCharacter.js

import React, { useEffect, useState } from 'react';
import $ from 'jquery';

function ShowCharacterInfo({dataCharacterDescription , dataFilms}) {
    return (
        <div>
            <p><b>name:</b>{dataCharacterDescription.name}</p>
            <p><b>hair color:</b>{dataCharacterDescription.hair_color}</p>
            <p><b>eye color:</b>{dataCharacterDescription.eye_color}</p>
            <ul>{dataFilms.map(dataFilm=><li key={dataFilm.title}>{dataFilm.title}</li>)}</ul>
        </div>
    )
}

export default function StarwarsCharacter({id}) {
    const [dataCharacterDescription , setDataCharacterDescription] = useState(null);
    const [dataFilms , setDataFilms] = useState(null);

    function pushDataToDataFilms(data) {
        setDataFilms(oldData=>[...oldData||[], data]);
    }
    
    useEffect(()=>{
        if(!dataCharacterDescription) return;
        const filmsUrls = dataCharacterDescription.films;
        const xhrFilms = filmsUrls.map(filmsUrl=>$.getJSON(filmsUrl , pushDataToDataFilms));
        return(()=>xhrFilms.forEach(xhrFilm => xhrFilm.abort()));
    },[dataCharacterDescription]);

    useEffect(()=>{
        setDataCharacterDescription(null);
        setDataFilms(null);
        const xhrDescription = $.getJSON(`https://swapi.dev/api/people/${id}/`, setDataCharacterDescription);
        return ()=> {xhrDescription.abort();}
    },[id]);

    return (
        <div>
            {dataCharacterDescription && dataFilms ? 
            <ShowCharacterInfo dataCharacterDescription = {dataCharacterDescription} dataFilms = {dataFilms}/> :
            'loading, please wait...'}
        </div>
    );
}

הי

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

לגבי הטיפול בביטול אני חושב שאפשר להוסיף catch אחרי ה then כדי לטפל בביטול הזה. הרעיון מתואר כאן:
https://javascript.info/promise-error-handling

לייק 1

היי אני מנסה להשתמש באתר https://swapi.dev/ אבל הוא לא עובד, שגיאת אבטחה של גוגל…
יש api אחר שעושה עבודה דומה?
תודה רבה

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

בינתיים את יכולה לטעון מידע מהאתר הזה:
https://pokeapi.co/

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

גם אני נתקלתי בבעיה הזו ונראה לי שעכשיו זה בסדר, האתר ההוא עובד.

היי ינון, אשמח לעזרתך בקוד שבניתי: הקוד אמור לטעון מידע ב-AJAX באמצעות USEEFEECT בדומה לסרטון. אלא שהפעם אני השתמשתי לא בספריית jQuery כפי שאתה מראה בסרטון - השתמשתי בפקודת ה-FETCHING. כמו כן אני משתמשת בספריית TAILWIND וב-YARN (ולא ב-NPM).
בקוד אמור להיות כפתור שבעת לחיצה עליו, יתבצע תהליך טעינת המידע כפי שמצויין למעלה.(כלומר אחרי שלחצתי על הכפתור אמור להיות מוצג לי על המסך המידע שהבאתי)
אשמח לעזרתך בתיקון הקוד משום שיש לי 2 בעיות:

  1. אני לא מצליחה להציג בכלל את המידע אחרי הלחיצה (מופיעה לי ההודעה : log.js:24 [HMR] Waiting for update signal from WDS…)
  2. אם אני רוצה להוריד את ה-count שלי הקוד לא עובד לי טוב
    תודה רבה!

הקוד:

import React from 'react';

import { useState, useEffect } from 'react';

export const Fetching = () => {

  const [ person, setPerson ] = useState({})

    const [ count, setCount ] = useState(0)

  

    useEffect(() =>

      fetch("https://swapi.dev/api/people/1/")

        .then(res => res.json())

        .then(setPerson),

    [])

  

    return(

      <div>

        <p className="name">{person.name}</p>

        <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4" onClick={() => setCount(count + 1)}>Get another book</button>

      </div>

    )

  }

export default Fetching;

הי @inbar700,

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

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

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

במצב כזה כדאי לשמור שני משתני State: באחד שומרים את ה id ובשני את המידע עצמו (עשיתי את זה גם בשיעור את יכולה לראות בוידאו שוב). בעליית הקומפוננטה תגדירי את ה id להיות null וכשאת לוחצת על הכפתור שמרי בתוכו ערך.

במקביל השתמשי ב useEffect כדי שכל פעם שה id ישתנה תתחיל פעולת fetch.

זה בדיוק הקוד שיש בוידאו רק תשתמשי ב fetch במקום ב jQuery והכל יסתדר

האם יש אפשרות לקבל בתוך ה useEffect את ה response.satus - כלומר 200, 302, 500 וכו

הי,

אתה משתמש ב fetch או בדרך אחרת לבצע את התקשורת?יכול להדביק כאן את הקוד?

הי ינון
אני משתמש ב jquery

useEffect(function() { 
    const $xhr = $.post( apiUrl + '/get_toing_list/', JSON.stringify(toings),
        function(result){
        },'json');
            
        return function abort(){
            $xhr.abort();                
        }
},[sendRequest]);

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

ושווה לנסות גם את הממשק החדש יותר - fetch - שכבר די יציב היום:
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

(למרות שלדעתי jQuery קצת יותר קל לשימוש)

מעולה, הוספתי את כל הפרמטרים לפונקציה וזה עובד
function(result, textStatus, jqXHR)

לייק 1

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

function ShowInfo(props) {
  const { data } = props;
  return (
    <>
      <p><b>Name:</b> {data.name}</p>
      <p><b>Hair Color:</b> {data.hair_color}</p>
      <p><b>films:</b></p>{data.films ? <ShowFilms data={data.films} /> : "loading fils title...."}
    </>
  );
}

function StarwarsCharacter(props) {
  const [data, setData] = useState({});
  const { id } = props;
  useEffect(function () {
    setData(null);
    const $xhr = $.getJSON(`https://swapi.dev/api/people/${id}/`, setData);

    return function abort() {
      $xhr.abort();
    }
  }, [id]);
  return (
    <div>
      <pre>Debug: id = {id}</pre>
      {data ? <ShowInfo data={data} /> : "loading..."}
    </div>
  );
}
function ShowFilms(props) {
  const { data } = props;
  const [filmsTitle, setFilmTitle] = useState([]);
  const filmsTitleArray = [];
  for (let i = 0; i < data.length; i++) {
    useEffect(function () {
      let url = data[i];
      const $xhr = $.getJSON(`${url}`, function (data) {
        filmsTitleArray.push(data.title)
        setFilmTitle([...filmsTitleArray]);
      });
      return function abort() {
        $xhr.abort();
      }
    }, [])
  }
  return (
    <ol>
      {filmsTitle.map(film => (
        <li key={film}>{film}</li>
      )
      )}
    </ol>

  )
}
const App = () => {
  const [id, setId] = useState(1);

  return (
    <div>
      <input type="number" value={id} onChange={(e) => setId(e.target.value)} />
      <StarwarsCharacter id={id} />
    </div>
  )
};

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