How to edit data received from a rest call in react? - reactjs

I am trying to make a rest call, edit the data and then render it. The problem is that, while editing, I am getting a error - undefined even after checking if the data is there.
component I am making the rest call from:
function Header ({timesheetData, loadTimesheet}) {
useEffect(() => {
loadTimesheet(date)
}, [])
return(
<>
<header className="header">
<div className="wrap">
<span className="btn-icon">
<IconPlus onClick={() => setIsOpen(true)} className="icon icon-plus js-modal-init"/>
</span>
<div className="header-blockquote">
<h1 className="header-quote">{currentQuote.quote}</h1>
<div className="header-cite">{currentQuote.author}</div>
</div>
</div>
<div className="header-inner">
<div className="wrap">
<VegaIcon className="logo" alt="VegaIT"/>
<div className="date-wrap">
<IconCalendar className="icon icon-plus js-modal-init"/>
//
<time>{timesheetData.timesheet.loading ? "asd" : edit(timesheetData) }</time>
//
</div>
</div>
</div>
</header>
</>
)
}
function edit(timesheetData){
let newDate = timesheetData.timesheet.date
newDate = newDate.split("-")
newDate = newDate.reverse()
return newDate.join("/")
}
the redux action:
export const loadTimesheet = (date) => {
let url = "http://localhost:8080/api/timesheet/" + date
return (dispatch) => {
dispatch(getTimesheet)
axios.get(url)
.then((response) => {
const timesheet = response.data
dispatch(getTimesheetSuccess(timesheet))
})
.catch(error => {
const errorMsg = error.message
dispatch(getTimesheetFailure)
})
}
}
Edit: added my mapStateToProps and mapDispatchToProps
const mapStateToProps = state => {
return {
timesheetData: state.timesheet
}
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: (date) => dispatch(loadTimesheet(date))
}
}
Edit2: The code: https://codesandbox.io/s/adoring-tharp-o9ibe

use mapStateToProps, mapDispatchToProps:
import getTimesheet from '../actions/...'
Header ({timesheetData, loadTimesheet}) => {
}
const mapDispatchToProps = dispatch => {
return {
loadTimesheet: () => dispatch(getTimesheet()),
}
}
const mapStateToProps = (state) => {
return { timesheetData: state.timesheetData };
};
export default connect(mapStateToProps, mapDispatchToProps)(Header);
also in component:
before render:
check that you have:
timesheetData - not undefined
timesheetData.timesheet - not undefined
timesheetData.timesheet.loading - not undefined
const hasData = timesheetData && !timesheetData.timesheet.loading;
const time = hasData ? edit(timesheetData): "asd";
in render:
<time>{time}</time>

Related

React: Uncaught TypeError: Cannot read properties of undefined (reading 'jpg')

I'm new to react, I'm trying to pick a random anime from jikan, which will return an object containing a key images(an object). images containg a key jpg. It keeps saying that "Cannot read properties of undefined (reading 'jpg')". Please help! Thank you so much
This is the main component:
import React, { useState, useEffect } from "react";
import "./animedb.css";
import TopAnime from "./topanime";
import AnimeSearch from "./animesearch";
import AnimeRandom from "./animerandom";
import AnimeDisplay from "./animedisplay";
import NavBar from "./navbar";
const AnimeDB = () => {
const [animeList, setAnimeList] = useState([]);
const [isRandomAnime, setIsRandomAnime] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [topAnime, setTopAnime] = useState([]);
const [searchAnime, setSearchAnime] = useState("");
const [randomAnime, setRandomAnime] = useState({});
const url = "https://api.jikan.moe/v3";
const ShowAnime = async () => {
let display_url = url + "/season/2022/spring";
const response = await fetch(display_url);
const result = await response.json();
setAnimeList(result.anime.slice(0, 10));
setIsLoading(false);
};
useEffect(() => {
ShowAnime();
FindTopAnime();
}, []);
const FindTopAnime = async () => {
let top_url = url + "/top/anime/1/bypopularity";
try {
const res = await fetch(top_url);
const result = await res.json();
setTopAnime(result.top.slice(0, 5));
} catch (error) {
console.log(error);
}
};
const FindSearchAnime = async (query) => {
setIsLoading(true);
try {
const res = await fetch(
`https://api.jikan.moe/v3/search/anime?q=${query}&order_by=title&sort=asc`
);
const result = await res.json();
setAnimeList(result.results.slice(0, 10));
setIsLoading(false);
} catch (error) {
console.log(error);
}
};
const handleSearch = (e) => {
e.preventDefault();
FindSearchAnime(searchAnime);
};
const handleRandom = async () => {
setIsLoading(true);
setIsRandomAnime(true);
let res = await fetch("https://api.jikan.moe/v4/random/anime");
let result = await res.json();
setRandomAnime(result.data);
setIsLoading(false);
console.log(result.data);
};
return (
<div className="wrapperAnime">
{/* <NavBar /> */}
<h1 id="appName">
u<strong id="strong">ME</strong>i
</h1>
<div className="container">
<TopAnime topAnime={topAnime} />
<div className="display">
<button onClick={handleRandom}>Random Anime</button>
<AnimeSearch
onSearch={handleSearch}
setSearchAnime={setSearchAnime}
value={searchAnime}
/>
<AnimeDisplay
isLoading={isLoading}
animeList={animeList}
isRandomAnime={isRandomAnime}
randomAnime={randomAnime}
setIsRandomAnime={setIsRandomAnime}
/>
</div>
</div>
</div>
);
};
export default AnimeDB;
component: AnimeDisplay:
function AnimeDisplay(props) {
if (props.isRandomAnime) {
let { mal_id, title, images } = props.randomAnime;
return (
<div className="collections">
<div key={mal_id} className="card">
<h1>{title}</h1>
<img src={images.jpg.image_url} alt="test" />
</div>
</div>
);
} else {
return (
<div className="collections">
{props.animeList.map((ele) => {
const { mal_id, image_url, title, episodes } = ele;
if (props.isLoading) {
return <h1>Loading...</h1>;
}
return (
<div key={mal_id} className="card">
<img src={image_url} alt="" />
<h1>{title}</h1>
</div>
);
})}
</div>
);
}
}
export default AnimeDisplay;
#Update: I've tried your ways, but it didn't work either. This is the returned data:
{mal_id: 43450, url: 'https://myanimelist.net/anime/43450/Shao_Nian_Effendi', images: {…}, trailer: {…}, title: 'Shao Nian Effendi', …}aired: {from: '2012-01-01T00:00:00+00:00', to: null, prop: {…}, string: '2012 to ?'}airing: falsebackground: nullbroadcast: {day: null, time: null, timezone: null, string: 'Unknown'}demographics: [{…}]duration: "12 min per ep"episodes: 104explicit_genres: []favorites: 0genres: []images: jpg: image_url: "https://cdn.myanimelist.net/images/anime/1248/118197.jpg"large_image_url: "https://cdn.myanimelist.net/images/anime/1248/118197l.jpg"small_image_url: "https://cdn.myanimelist.net/images/anime/1248/118197t.jpg"[[Prototype]]: Objectwebp: {image_url: 'https://cdn.myanimelist.net/images/anime/1248/118197.webp', small_image_url: 'https://cdn.myanimelist.net/images/anime/1248/118197t.webp', large_image_url: 'https://cdn.myanimelist.net/images/anime/1248/118197l.webp'}[[Prototype]]: Objectlicensors: []mal_id: 43450members: 31popularity: 19210producers: []rank: 14029rating: "PG - Children"score: nullscored_by: nullseason: nullsource: "Other"status: "Finished Airing"studios: []synopsis: nullthemes: []title: "Shao Nian Effendi"title_english: nulltitle_japanese: "少年阿凡提"title_synonyms: (3) ['Shao Nian A Fan Ti', 'Shao Nian Afanti', 'Young Effendi']trailer: {youtube_id: null, url: null, embed_url: null, images: {…}}type: "TV"url: "https://myanimelist.net/anime/43450/Shao_Nian_Effendi"year: null[[Prototype]]: Object
I think it's because your initial state doesn't have any "jpg" key and when it reads your code for the first time (before useEffect) it throws an error. you could either (the wrong way!) update your initial state like so: useState([{images:{jpg:""}}]) or the right way which is conditional rendering!
As Shahryar_Jahanshahloo said, you must provide a default value for your initial state, so change the randomAnime this way:
// note that the object is much more greater than this, but I only included
// the properties that you are using
const [randomAnime, setRandomAnime] = useState({
mal_id: "",
title: "",
images: { jpg: { image_url: "" } }
});
Another thing I notice that is wrong is your loading state in your AnimeDisplay, you should do something like this:
function AnimeDisplay(props) {
const { mal_id, title, images } = props.randomAnime;
// move the loading conditional to outside of the mapping
// so when the component is fetching the data it only
// renders the loading element
if (props.isLoading) {
return <h1>Loading...</h1>;
}
// after the fetch you can render your components normally
if (props.isRandomAnime) {
return (
<>
<div className="collections">
<div key={mal_id} className="card">
<h1>{title}</h1>
<img src={images?.jpg.image_url} alt="test" />
</div>
</div>
</>
);
} else {
return (
<div className="collections">
{props.animeList.map((ele) => {
const { mal_id, image_url, title, episodes } = ele;
return (
<div key={mal_id} className="card">
<img src={image_url} alt="" />
<h1>{title}</h1>
</div>
);
})}
</div>
);
}
}

React useState async setter doesn't update value passed as props

I have this component in my React project -
const ViewPost = (props: Props) => {
const [listingData, setListingData] = useState<any>({})
const [auctionData, setAuctionData] = useState<any>({})
useEffect(() => {
if (props.listingId) {
getListingData()
}
}, [props.listingId])
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
setListingData(data?.data)
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
const auctions = async (auctionId: any) => {
const auction = await getAuctions(auctionId)
console.log('auction', auction.data)
setAuctionData(auction.data)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}
export default ViewPost
Basically, I'm getting data from an API and assigning it to auctionData.
console.log(auction.data) shows me the desired result but when I pass auctionData as props into Details I get an empty object which leads to a lot of issues, since useState is async.
How can I overcome this problem?
const [auctionData, setAuctionData] = useState<any>({})
your default value is an empty object, that causes the problems.
should set null or undefined as default value, and hide the Details when not have the data.
Use loading state. Once data is fully fetched from api then pass to child component. I think what is happeing here is that child component is called with empty state variable while data is still being fetched.
const [isLoading, setIsLoading] = useState(true)
const getListingData = async () => {
const { data } = await getListingById(props.listingId)
.then((data) => {setListingData(data)})
.then((data) => {
setTimeout(() => {
setIsLoading(false)
}, 1000)
})
if (data.data.isTimedAuction) {
auctions(data.data.auctionId)
}
}
and then return
if (isLoading) {
return (
<div>
Loading...
</div>
)
}
return (
<>
<Navbar />
<div className={classes.viewPostPage}>
<div className={classes.bodyContainer}>
<Details
data={listingData as any}
updateListing={getListingData}
auctionData={auctionData}
/>
</div>
</div>
</>
)
}

Add a Spinner while waiting for promise to resolve in react

const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const getRandomMusic = () => {
return fetchMusic().then((result) => result.data);
};
const Button = (props) => {
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
<SomeComponent />
<p>Some text here</p>
</div>
);
};
I want add a spinner while waiting for the promise to resolve .
fetchMusic is in some other file.I m importing it in a component .
TLDR
How about use useState and useCallback for that action
Answer
At terms of react, use State for loading action is right use case.
So, When to start function, use can setLoading(true) and after action you can setLoading(false) for make loading effect
const fetchMusic= () => {
return new Promise((resolve) =>
setTimeout(() => {
const music = musicList.sort(() => 0.5 - Math.random()).slice(0, 4);
resolve({ data: music});
}, 300)
);
};
export default fetchMusic;
const Button = (props) => {
const [loaidng, setLoading] = useState(false);
const getRandomMusic = useCallback(() => {
setLoading(true)
return fetchMusic().then((result) => {
setLoading(false);
result.data
});
},[]);
return (
<div>
<Button {...props} onClick={getRandomMusic.bind(this)} />
{loading && <Sipinner/>}
<SomeComponent />
<p>Some text here</p>
</div>
);
};
Reference
Example of loading
ETC
If you have any other question. Just give me comment please.

React-Redux: Cannot read property 'map' of undefined when deleting an item

I have an error after clicking the delete button saying:
Cannot read property 'map' of undefined.
I'm new in React Redux JS.
Please see my code below of my component reducers and actions:
Post.js
class Post extends Component {
constructor(){
super();
this.deletePost = this.deletePost.bind(this);
}
deletePost(postId){
this.props.deletePost(postId);
}
render(){
const postItems = this.props.posts.map(post => (
<div key={post.id} className="row">
<div className="container">
<h3>{post.title}</h3>
<p>{post.body}</p>
<button
onClick={() =>this.deletePost(post.id)}
className="btn btn-danger">
Delete
</button>
</div>
</div>
))
const divStyle = {
padding: '15px',
}
return (
<div style={divStyle}>
<PostForm />
<hr/>
{postItems}
</div>
)
}
}
const mapStateToProps = state => ({
posts: state.posts.items,
newPost: state.posts.item
})
export default connect(mapStateToProps, { fetchPosts, deletePost })(Post);
PostAction.js (Here is my delete action. I am using jsonplaceholder API post.)
export const deletePost = (postId) => dispatch => {
fetch('https://jsonplaceholder.typicode.com/posts/'+postId, {
method: 'DELETE',
})
.then(dispatch({
type: DELETE_POST,
payload: postId
}));
}
PostReducer.js (This is my reducer.)
case DELETE_POST:{
const newState = Object.assign([], state);`enter code here`
const filteredItems = newState.items.filter(items => {
return items.id != action.payload;
});
return filteredItems;
}
case DELETE_POST:{
const { items } = state;
const filteredItems = items.filter(items => {
return items.id != action.payload;
});
return {
...state,
items: [ ...filteredItems ]
};
}
Yes just replace
return filteredItems; to return { items: filteredItems }
But please can you check my code if it's correct. Thanks

props.deleteComments not a function

I'm following some code from a guide and I'm getting that error.
In my App.js I have:
deleteComments = async (commentId) => {
try {
await axios.delete(`/comments/${commentId}`);
const comments = await this.getComments();
this.setState({ comments });
} catch (error) {
console.log(error);
}
};
CommentsList component
const CommentsList = (props) => {
const comments = props.comments.map((comment) => {
return (
<Comment
{...comment}
deleteComments={props.deleteComments}
key={comment.id}
/>
);
});
Comment component where I call the function.
import React from 'react';
const Comment = (props) => {
const deleteComments = () => {
props.deleteComments(props.id);
};
return (
<div>
...
<div>
<button onClick={deleteComments}>Delete</button>
</div>
</div>
);
};
It's quite obvious that CommentList doesn't get deleteComments prop passed. You need to pass it:
<CommentList deleteComments={deleteComments} />

Resources