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


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

חייב עזרה בסעיף שלוש אין לי כל כך כיוון לאיך לעשות את זה …
יש לי פקד שמציג פרטי שחקן ופקד שמציג פרטי סרט
הבעיה שלי היא איך לעשות דרך הפקד שמציג פרטי שחקן 5 קריאות לשרת ולהציג את הפרטים שחזרו

למה השחקן צריך להוציא את קריאות ה Ajax? לא עדיף ששחקן יוציא קריאות בשביל השחקן, וסרט יוציא קריאות בשביל הסרט?

זה מה שקורה כרגע
אני צריך עזרה איך להמשיך מפה
יש לי פקד שחקן שמקבל data ומציג
ויש פקד פרטי סרט שמציג את פרטי הסרט
בסעיף 3 אני אמור לקחת מה data של השחקן את כל הקישורים שמייצגים את הסרטים שהשחקן שאני נמצא עליו כרגע שיחק בהם
ולהוציא בקשות ajax כדי לקבל את פרטי הסרטים…

רוצה להדביק כאן את הקוד שכבר כתבת שאוכל להבין טוב יותר למה אתה מתכוון?

import React from 'react';

import ReactDOM from 'react-dom';

import $ from 'jquery';

import { useState, useEffect } from 'react';

function ShowCharcter(props){

    const {data } = props;

    return(

        <>

        <p><b>Name:</b> {data.name}</p>

        <p><b>Hair Color:</b> {data.hair_color}</p>

        <h2>List Of Movie Charcter Play :</h2>

        

        </>

    )

}

function ShowMovie(props){

  const {data} = props;

  return(

      <>

      <p><b>Name Of Movie:</b> {data.title}</p>

      <p><b>Name Of Director:</b> {data.director}</p>

      <p>..................................................</p>

      </>

  )

}

function StarwarsCharacter(props) {

  const [data, setData] = useState(null);

  const { id ,item } = props;

  useEffect(function (){

    setData(null);

    const $xhr = $.getJSON(`https://swapi.co/api/${item}/${id}/`,setData);

    return function abort(){  

        $xhr.abort();

    }

  },[item,id]);

  return (

    <div>

      <pre>Debug: id = {id} {item}</pre>

      {data ? item=="people" ?<ShowCharcter data={data}/>:<ShowMovie data={data}/>:"loading please wait .."}

    </div>

  );

}

const MyAjax = () => {

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

  const [item,setItem] = useState(null)

  

  return (

    <div>

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

      <select onChange={e => setItem(e.target.value)}> 

          <option disabled selected >{"Choose .."}</option>

          <option value={"films"}>Films</option>

          <option value={"people"}>people</option>

      </select>

      <StarwarsCharacter id={id} item={item} />

    </div>

  )

};

export default MyAjax;

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

זה ממש מסקרן אותי איך לעשותדבר כזה אשמח לתשובה…

הי,

אתה יכול לנסות לחבר את StarwarsCharacter ואת ShowCharcter לקומפוננטה אחת. אחרי זה צור קומפוננטה דומה לסרטים
(כרגע הקומפוננטה של הסרטים אצלך עושה רק חצי מהעבודה: היא מציגה נתוני סרט אבל אף אחד לא מביא נתונים אלה)

אין אפשרות כזאת לרוץ בלולאה מספר פעמים ולקרוא ל fetch עם url אחר כל פעם?

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

<Person [id=1, data]>
  <Movie [id=2, data] />
  <Movie [id=2, data] />
  <Movie [id=3, data] />
</Person>

כלומר שהקומפוננטה שמייצגת דמות תחזיר קומפוננטות לסרטים לפי הסרטים שהדמות משתתפת בהן. זה מאוד דומה ליצירת הרשימה בשיעור הזה:
https://www.tocode.co.il/bundles/react/lessons/key

כלומר בתוך הקוד של StarwarsCharacter היינו רוצים לראות פקודת map שמייצרת את רשימת כל הסרטים.

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

אני חושב שזה בדיוק מה שעשיתי …
מה ששאלתי סהכ זה איך אני עושה עם map קריאות לשרת …

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

מצרף קישור לדקומנטציה, אם יש לך הפניה למקור למידה אחר או הסבר טוב אשמח

הי,

התוכנית שמעניינת אותנו מתוך הדוקומנטציה היא זאת:

import Player from '@vimeo/player';

const player = new Player('handstick', {
    id: 19231868,
    width: 640
});

player.on('play', function() {
    console.log('played the video!');
});

זו תוכנית שטוענת סרט עם id שכתוב שם, לתוך div שהמזהה שלו הוא handstick ומוסיפה אירוע שכשמתחיל לנגן את הסרט מדפיסים הודעה למסך.

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

אשמח שתדביק פה את הקוד שתכתוב ואז נמשיך יחד את התרגיל

function YoutubePlayer(props) {
  const [videoId, setVideoId] = useState(19231868);
  const playerDivRef = useRef(null);

  useEffect(function() {
    const player = new Player(playerDivRef.current, {
      id: videoId,
      width: 640
    });

    player.on('play', function() {
      console.log('played the video!');
  });
  }, []);

  return (
    <div>
      <div>
      <input type="text" value={videoId} onChange={(e) => setVideoId(e.target.value)} />
      </div>
      <div className='handstick' ref={playerDivRef} />
    </div>
  );
}

const App = () => {
  return (
    <div>
      <h1>my component</h1>
      <YoutubePlayer />
    </div>
  )
};
לייק 1

בינתיים מעולה - רק שנה את השם ל VimeoPlayer (במקום ה YoutubePlayer)

שלב 2 - בוא ניקח את מזהה הסרט מ props במקום להשתמש בקבוע 19231868. הפונקציה loadVideo של הנגן מאפשרת לנו לטעון סרט חדש לתוך ה div.

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

שאלה מס’ 1:
(יש כמה Errors בלוג אבל סהכ עובד)

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';
import Player from '@vimeo/player';
import { useState, useRef } from 'react';

import '../css/main.css';

const VimeoPlayer = () => {
  const [videoId, setVideoId] = useState(76979871);
  const [isPlaying, setIsPlaying] = useState(true);
  const playerDivRef = useRef(null);
  const playerRef = useRef(null);

  useEffect(function() {
    playerRef.current = new Player(playerDivRef.current, {
      id: videoId,
      width: 640    
    });
  }, []);

  useEffect(function() {
    const player = playerRef.current;
    player.loadVideo(videoId);
    player.play();
  }, [videoId]);

  function toggleVideo(){
    const player = playerRef.current;
    if(isPlaying){
      setIsPlaying(false);
      player.pause();
    }
    else {
      setIsPlaying(true);
      player.play();
    }
  }



  return (
    <div>
      <h1> Enjoy the video!</h1>
      <input type="text" value={videoId} onChange={(e)=> setVideoId(e.target.value)} />
      <div className="player-div" ref={playerDivRef}/>
      <div>
        <button onClick={toggleVideo} >Play/Pause</button>
      </div>
    </div>
  )
}


const App = () => {

  return (
    <>
      <VimeoPlayer />
    </>
  )
};

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

איך הקוד שלי של שאלה מס’ 2?

import React from 'react';
import ReactDOM from 'react-dom';
import Player from '@vimeo/player';
import $ from 'jquery';
import { useState, useEffect } from 'react';

import '../css/main.css';

//customHook for code reuse
function useData(id, isPepole){
  const [data, setData] = useState(null);

  useEffect(function() {
    if(isPepole) {
      const $xhr = $.getJSON(`https://swapi.dev/api/people/${id}`, setData)
        .fail(() => setData(null))
    }
    else {
      const $xhr = $.getJSON(`${id}`, setData);
    }
  
    return function abort() {
      setData(null);
    }
  }, [id]);

  return data;
}

const DisplayMovie = (props) => {
  const { data } = props;

  return (
    <div className="container">
      <h1 className="film-name">{data.title}</h1>
      <h3>{data.director}</h3>
      <h3>{data.producer}</h3>
      <h4>{data.release_date}</h4>
      <p>{data.opening_crawl}</p>
      <h4>More Characters:</h4>
      <ul>
      {data.characters.map((item, index)=> (
      <li key={index}>{item}</li>
      ))}
      </ul>
    </div>
  )
}

const MovieDes = (props) => {
  const { id } = props;
  const data = useData(id, 0);

  return (
    <div>
      {data ? <DisplayMovie data={data}/> : <h1>loading...</h1>}
    </div>
  )
}

const DisplayChar = (props) => {
  const { data } = props;

  return (
    <div className="container">
      <h1 className="char-name">{data.name}</h1>
      {data.films.map((item, index)=> (
        <MovieDes key={index} id={item} />
      ))}
    </div>
  )
}

const CharDes = (props) => {
  const { id } = props;
  const data = useData(id, 1);
  
  return (
    <div>
      <p>id debug: {id}</p>
      {data ? <DisplayChar data={data} /> : <h1>Loading...</h1>}
    </div>
  )
}

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

  return (
    <>
      <input 
        type="number" 
        min='1'
        value={id} 
        onChange={(e)=>{setID(Number(e.target.value))}}
      />
      <CharDes id={id}/>
    </>
  )
};

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

הי,

לגבי הנגן - בדרך כלל באפקטים נהוג להחזיר פונקציית ״ניקוי״ שמבטלת את מה שהאפקט עשה. אם אם יש לנו אפקט שיוצר נגן, פונקציית הניקוי תקרא ל destroy של הנגן כדי למחוק אותו:
https://developer.vimeo.com/player/sdk/reference#destroy-a-player

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

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

function loadVideo(videoId) {
  setVideoId(videoId);
  const player = playerRef.current;
  player.loadVideo(videoId);
  player.play();
}

ואז להפעיל אותה מה onChange של תיבת הטקסט


לגבי שאלה 2 - אני רואה שהשתמשת במשתנה isPeople כדי להבין אם הפרמטר הראשון הוא url מלא או רק id. עדיף לבדוק את זה דינמית עם ביטוי רגולארי או תנאי פשוט (האם אפשר להמיר את מה שקיבלתי למספר למשל), וככה לקבל קוד יותר גנרי