זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/32-code-sharing-lab
זהו נושא דיון מלווה לערך המקורי שב־https://www.tocode.co.il/bundles/react/lessons/32-code-sharing-lab
בעיה קטנה
הקוד שלי פונה לשרת לקבל ה data שאני מקבל את ה data אני הופך אותה לjson
כאשר אני פונה name שנמצא בתוך json הוא נותן לי שגיאה
Cannot read property ‘name’ of undefined
ה json שלי נראה ככה …
{“name”:“Luke Skywalker”,“height”:“172”,“mass”:“77”,“hair_color”:“blond”,“skin_color”:“fair”,“eye_color”:“blue”,“birth_year”:“19BBY”,“gender”:“male”,“homeworld”:“https://swapi.co/api/planets/1/",“films”:[“https://swapi.co/api/films/2/”,“https://swapi.co/api/films/6/”,“https://swapi.co/api/films/3/”,“https://swapi.co/api/films/1/”,“https://swapi.co/api/films/7/”],“species”:[“https://swapi.co/api/species/1/”],“vehicles”:[“https://swapi.co/api/vehicles/14/”,“https://swapi.co/api/vehicles/30/”],“starships”:[“https://swapi.co/api/starships/12/”,“https://swapi.co/api/starships/22/”],“created”:“2014-12-09T13:50:51.644000Z”,“edited”:“2014-12-20T21:17:56.891000Z”,“url”:"https://swapi.co/api/people/1/”}
import React from ‘react’;
import useFetch from ‘use-http’
const useRemoteData = (url,id) => {
const request = useFetch("https://swapi.co/api/people/1/");
request.get();
return [request.data,request.loading,request.error];
}
function StarwarsCharacter(props) {
const { id } = props;
const [data, isLoading, error] = useRemoteData(`https://swapi.co/api/people/${id}/`, [id]);
if (error) {
return <p className='error'>{error}</p>
}
if (isLoading) {
return <p>Please wait, loading data...</p>
}
return (
<div>
<p>Character name: {JSON.stringify(data)}</p> עובד
<p>Character name: {JSON.stringify(data).name}</p> לא עובד
</div>
);
}
export default StarwarsCharacter;
נשמע שב json שלך יש שדה בשם name. יכול להיות שהשגיאה מופיעה אם אתה מנסה לגשת ל json לפני שקיבלת את הערך מהשרת?
היי:) יכול להעיף מבט על הקוד שלי… משום מה המידע לא חוזר כמו שצריך מהשרת… איפה הבעיה?
הי,
היו כמה דברים מוזרים בקוד-
1.המשתנה output לא צריך להיות משתנה state, במקום זה צריך פשוט להחזיר מערך.
2. לא צריך לקבל ID בפונקציה. היא לא עושה איתו כלום. מספיק לקבל את ה URL.
3. ה API שלך לפעמים מחזיר שם ריק, אז הוספתי עוד שדה של Culture שנמצא ביותר דמויות.
זה הקוד אחרי תיקון:
מעולה תודה רבה! אגב הAPI המקורי של מלחמת הכוכבים לא עובד יותר משום מה… בגלל זה עברתי למשחקי הכס:slight_smile:
נראה לי שאתה צריך לעדכן גם את התרגילים בכל הפרקים שבהם השתמשת או ביקשת להשתמש בו…
כן הוא הוריד את זה ממש לפני כמה ימים. תרגמתי את הדוגמא לפוקימון ועוד לא הספקתי לעדכן את התרגילים
היי,
שאלה קטנה:
משום מה אני לא מצליחה לטעון תמונות לפרוייקט שלי. התקנתי את url loader ועדיין ברגע שאני מנסה להוסיף תמונה מופיעה לי שגיאה :
Module parse failed: Unexpected character ‘�’ (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
צריך להגדיר אותו. יש הוראות כאן:
רוצה לפרסם את ה webpack.config.js שלך נראה אם זה מוגדר טוב?
זה הקובץ webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/main.js',
mode: 'development',
devServer: {
overlay: true,
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
}
}
},
{
test: /\.css$/,
use: [
{ loader: MiniCssExtractPlugin.loader, },
'css-loader',
],
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
use:[
{loader: 'url-loader?limit=100000' }
]
},
]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './html/index.html',
}),
new MiniCssExtractPlugin({
filename: '[name].css',
chunkFilename: '[id].css',
ignoreOrder: true,
}),
new CleanWebpackPlugin(),
]
};
מסתבר שהייתי צריכה אחרי העריכה של הקובץ לשמור ולהריץ שוב דרך CMD.
כרגע לא מתקבל השגיאה אבל עדיין התמונה לא מוצגת על המסך.
איך נראה הקוד?
אגב מה שיהיה הכי קל זה אם תעלי את הקוד כולו לגיטהאב או משהו שאפשר יהיה להסתכל על כל הקבצים
מצרפת את התרגילים - אשמח להערות,
תודה רבה!
תרגיל 1.)
imagesData.js
const imagesData = {
img1: {
src:"https://www.photo-art.co.il/wp-content/uploads/2018/09/J85_9560.jpg" ,
alt:"img1",
},
img2: {
src:"https://www.photo-art.co.il/wp-content/uploads/2015/06/BY1A14381-420x279.jpg" ,
alt:"img2",
},
img3: {
src:"https://www.photo-art.co.il/wp-content/uploads/2017/10/SY201703-157249-420x280.jpg" ,
alt:"img3",
},
img4: {
src:"https://www.photo-art.co.il/wp-content/uploads/2015/07/BY1A0473-420x281.jpg" ,
alt:"img4",
},
img5: {
src:"https://images.squarespace-cdn.com/content/v1/5a7c0544d74cffa3a6ce66b3/1581028716769-K1Y7KURGOFNJ8LW6ZHOF/ke17ZwdGBToddI8pDm48kEc6DKFOx3cpvkyhZQwPkJYUqsxRUqqbr1mOJYKfIPR7LoDQ9mXPOjoJoqy81S2I8N_N4V1vUb5AoIIIbLZhVYxCRW4BPu10St3TBAUQYVKc53GO0vWykWK-lIOAtsaJ7vbB1JiXq7byw-6Ogo-kuNnXzR_WIuWSVqqqoqhN8EA5/%D7%AA%D7%9E%D7%95%D7%A0%D7%AA+%D7%A0%D7%95%D7%A3+-+%D7%A9%D7%95%D7%95%D7%99%D7%A5.jpg?format=2500w" ,
alt:"img5",
},
img6: {
src:"https://www.photo-art.co.il/wp-content/uploads/2015/07/IMG_7877-420x280.jpg" ,
alt:"img6",
},
img7: {
src:"https://d3m9l0v76dty0.cloudfront.net/system/photos/1493514/show/6e9573343bd22341277b0e331782c4d2.jpg" ,
alt:"img7",
},
img8: {
src:"https://www.photo-art.co.il/wp-content/uploads/2015/06/BY1A65881.jpg" ,
alt:"img8",
},
img9: {
src:"https://www.photo-art.co.il/wp-content/uploads/2015/07/IMG_7877-420x280.jpg" ,
alt:"img9",
},
};
export default imagesData;
useClock.js
import { useEffect } from 'react';
export default function useClockActivatesFunction(ms , fn) {
useEffect(()=>{
const timerId = setInterval(fn , ms);
return ()=> clearInterval(timerId);
},[]);
}
main.js
import React from 'react';
import ReactDOM from 'react-dom';
import Carousel from './carousel';
import imagesData from './imagesData';
const App = () => {
return (
<>
<h1 style={{textAlign:"center"}}>Carousel</h1>
<Carousel>
{
Object.keys(imagesData).map(imgKey=>(
<img key={imgKey} src={imagesData[imgKey].src} alt={imagesData[imgKey].alt}/>
))
}
</Carousel>
</>
);
}
ReactDOM.render(<App/> , document.querySelector('main'));
carousel.js
import React, { useRef, useState } from 'react';
import useClockActivatesFunctionfrom './useClock';
export default function Carousel(props) {
const [left , setLeft] = useState(0);
useClockActivatesFunction(5000 , updateNextLeft);
const imagesCount = React.Children.count(props.children);
const imageClass = useRef('');
const imgWidth = 1000;
function updateNextLeft() {
imageClass.current = 'animate__animated animate__slideInRight';
setLeft(v => v === (imgWidth * (imagesCount-1) * -1) ? 0 : v - imgWidth);
}
function updatePreviousLeft() {
imageClass.current = 'animate__animated animate__slideInLeft';
setLeft(v => v === 0 ? (imgWidth * (imagesCount-1) * -1) : v + imgWidth);
}
const btnStyle = {width:"50%" , backgroundColor:"#5050da" , color:"white"};
const meshStyle = {width:`${imgWidth}px`, height:"400px" , position:"relative" , marginLeft: "calc(50vw - 500px)" , overflow:"hidden" , backgroundColor:"black"};
const filmStyle = {width:"999999px" , height:"400px" , position:"absolute" , left:`${left}px`};
const imageStyle = {width:`${imgWidth}px` , height:"400px"};
return (
<>
<div style={meshStyle}>
<div style={filmStyle}>
{props.children.map((child , index)=>
React.cloneElement(child , {style : imageStyle , className: index === (left / (imgWidth*-1)) ? imageClass.current : ''})
)}
</div>
</div>
<div style={{width:`${imgWidth}px` , margin:"auto"}}>
<button style={btnStyle} onClick={()=>updatePreviousLeft()}>Previous</button>
<button style={btnStyle} onClick={()=>updateNextLeft()}>Next</button>
</div>
</>
);
}
תרגיל 2.)
import { useEffect, useState } from 'react';
import $ from 'jquery';
export default function useRemoteData(url) {
const [data , setData] = useState(null);
const [error ,setError] = useState(null);
const isLoading = data ? false : true;
useEffect(()=> {
setData(null);
setError(null);
const xhr = $.getJSON(url , setData).fail((jqxhr, textStatus, error)=>setError(`Request Failed: ${textStatus}. ${error}`));
return()=>xhr.abort();
},[url]);
return [data , isLoading , error];
}
נראה מדויק. כמה דברים קטנים:
-
את useRef בדרך כלל מאתחלים עם null ולא עם מחרוזת ריקה
-
את כל משתני העיצוב (btnStyle, meshStyle, filmStyle) הייתי מצרף לאוביקט אחד וקורא לו למשל theme:
const theme = {
button: {
width: '50%',
backgroundColor: '#5050da',
// ...
}
};
היי ינון, אשמח גם לקבל הערות על הקוד
app:
import Carousel from './component/Carousel';
import Images from './component/images';
import './App.css';
function App() {
const imagesData = [
{
src: "https://www.photo-art.co.il/wp-content/uploads/2018/09/J85_9560.jpg",
alt: "img1",
},
{
src: "https://www.photo-art.co.il/wp-content/uploads/2015/06/BY1A14381-420x279.jpg",
alt: "img2",
},
{
src: "https://www.photo-art.co.il/wp-content/uploads/2017/10/SY201703-157249-420x280.jpg",
alt: "img3",
},
]
return (
<div className="App">
<Carousel >
<Images src={imagesData[0].src}></Images>
<Images src={imagesData[1].src}></Images>
<Images src={imagesData[2].src}></Images>
</Carousel>
</div>
);
}
export default App;
images:
import React, { useState } from "react";
export default function Images(props){
const{src}=props
return(
<div>
<img src={src} style={{width:"500px",height:"500px"}}></img>
</div>
)
}
Carousel:
export default function Carousel(props) {
const [currentPage, setCurrentPage] = useState(0);
const numberOfPages = React.Children.count(props.children);
function pageComponent(pageIndex) {
const child = React.Children.toArray(props.children)[pageIndex];
return React.cloneElement(child);
}
return (
<div>
<button
disabled={currentPage === 0}
onClick={(e) => setCurrentPage(v => v - 1)}
>< Previous Page
</button>
<button
disabled={currentPage >= numberOfPages - 1}
onClick={(e) => setCurrentPage(v => v + 1)}
>Next Page ></button>
{ pageComponent(currentPage) }
</div>
)
}
הי חיה
בגדול עובד אבל כמה דברים שהייתי עושה אחרת-
-
מידע גלובאלי אני אוהב להוציא מחוץ לקומפוננטות ריאקט. זה אומר ש imagesData לא צריך לשבת בתוך App. אם המערך הזה נמצא באיזה קובץ חיצוני אנחנו מרגישים יותר בנוח לשנות אותו בלי לחשוש ששוברים את הקומפוננטה.
-
אני מעדיף לכתוב אוביקטי style בתור משתנה מאשר כ Inline, שוב מבחינת תחזוקה, לכן ב Images הייתי כותב:
const imageStyle = { width: '500px', height: '500px' };
return (
<div>
<img src={src} style={imageStyle} />
</div>
);
- ובקרוסלת התמונות אין צורך לקרוא ל cloneElement כיוון שאת לא משנה מאפיינים של האלמנט. אפשר פשוט להחזיר את child.
חוץ מזה נראה מעולה.