import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { useGlobalContext } from "../context";
const SingleTvShow = () => {
const { id } = useParams();
const [details, setDetails] = useState({});
const { imgUrl } = useGlobalContext();
const getDetails = async (showId) => {
const resp = await fetch(
`https://api.themoviedb.org/3/tv/${showId}?api_key=API_KEY&language=en-US`
);
const data = await resp.json();
setDetails(data);
};
useEffect(() => {
getDetails(id);
}, [id]);
return (
<div>
{console.log(details)}
<img src={imgUrl + details.backdrop_path} alt="show" />
</div>
);
};
export default SingleTvShow;
https://drive.google.com/file/d/1FhYcJSqZiko0lJBoQdB2Gx5KyMWGR3dj/view?usp=sharing
I just started learning react, please help
Because the initial value of state details is empty {}. details only update when you call api success. Before that, details kept the value {}
You can check like this:
{details.backdrop_path && <img src={imgUrl + details.backdrop_path} alt="show" />}
As #Viet said - the initial value is an empty object {} that is being populated when you have fetched the data.
If you want to show the data to your UI only when the fetching process has been completed then i would suggest changing your return to this:
return (
<div>
{console.log(details)}
{details && <img src={imgUrl + details.backdrop_path} alt="show" /> }
</div>
);
This tells your function to only show the the img element if your state is not an empty object.
Related
I am having a hard time getting my React App working properly.
The thing is that I tried to use UseEffect hooks only to run side effects in my app and this has brought me some problems.
In this simple component I have a chat that get data from Firebase and is capable of updating the Db. I have no problem with the Firebase side but on the front end, the first render is not able to get me the messages into state properly.
I feel that it has of course something to do with async behaviors.
I will try to explain you the flow of my component :
The message text is kept in a const in state call "inputText"; when the form is submited a const call "numberOfMessageSent" is incremented; I have a UseEffect Hook that has [numberOfMessageSent] in its depedency; so after the first mount of the component and when "NumberOfMessageSent" increments the callback will fire; this callback fires 2 async functions: one to fetch the current discussion from the db and another to create a discussion object or update an existing one into the Db. I have a condition :
"numberOfMessagesSent !== 0 && asyncWarperCreateDiscussionInDb()" in the UseEffect Hook so a new discussion empty discussion won't be created the first this component mount.
My problem is that no discussion is displayed (nor properly fetched and stored into state) BEFORE I send a first message. After I send this first message everything works properly.
Can someone help me to understand this better ?
Thank you very much
here is my code :
import React, { useContext, useEffect, useState } from "react";
import "./card-medium-message.style.scss";
import likeEmpty from "./like-empty.png";
import likeFull from "./like-full.png";
import cancel from "./cancel.png";
import send from "./send.png";
import back from "./back.png";
import { useNavigate, useParams } from "react-router-dom";
import { UsersListContext } from "../../context/usersList-context/users-list-context";
import { UserContext } from "../../context/user-context/user-context";
import {
createDiscussionInDb,
goFetchDiscussionInDb,
goFetchDisscussion,
} from "../../utils/firebase";
const CardMediumMessage = () => {
const params = useParams();
const { usersListCTX } = useContext(UsersListContext);
const { currentUserContext } = useContext(UserContext);
const currentUserClickedOn = usersListCTX.filter(
(user) => user.displayName === params.name
);
console.log(currentUserContext);
console.log(currentUserClickedOn[0]);
const [messages, setMessages] = useState([]);
const [inputText, setInputText] = useState("");
const [numberOfMessagesSent, setNumberOfMessagesSent] = useState(0);
const asyncWarperFetchDiscussionInDb = async () => {
if (currentUserClickedOn[0]) {
const discussion = await goFetchDiscussionInDb(
currentUserContext.displayName,
currentUserClickedOn[0].displayName
);
setMessages(discussion.messages);
}
};
const asyncWarperCreateDiscussionInDb = async () => {
await createDiscussionInDb(
currentUserContext.displayName,
currentUserClickedOn[0].displayName,
inputText
);
resetField();
};
useEffect(() => {
numberOfMessagesSent !== 0 && asyncWarperCreateDiscussionInDb();
asyncWarperFetchDiscussionInDb();
console.log(
"this is written after first render of the component or numberOfMessagesSent was updated"
);
}, [numberOfMessagesSent]);
const messageSubmit = async (e) => {
e.preventDefault();
if (inputText == "") {
return;
}
setNumberOfMessagesSent(numberOfMessagesSent + 1);
};
const textChanged = (e) => {
setInputText(e.target.value);
};
const resetField = () => {
setInputText("");
};
const navigate = useNavigate();
messages && console.log(messages);
return (
<div className="card-medium-warp">
<div className="card-medium-message">
<div className="section1" onClick={() => navigate(-1)}>
<div className="profile-image-outer-circle">
{currentUserClickedOn[0] ? (
<img
src={`https://api.dicebear.com/5.x/micah/svg?seed=${currentUserClickedOn[0].displayName}`}
alt="avatar"
className="profile-image"
/>
) : undefined}
</div>
{currentUserClickedOn[0] ? (
<h2 className="name">{currentUserClickedOn[0].displayName} </h2>
) : undefined}
<div
className="back"
style={{ backgroundImage: `url(${back})` }}
></div>
</div>
<div className="section2">
{messages
? messages.map((messageObject, index) => (
<p
key={index}
className={
messageObject.by === currentUserContext.displayName
? "sender-message"
: "receiver-message"
}
>
{messageObject.message}
</p>
))
: undefined}
</div>
<form className="section3" onSubmit={messageSubmit}>
<input
type="text"
className="input"
placeholder="your message"
onChange={textChanged}
value={inputText}
autoFocus
/>
<div
className="send-message"
style={{ backgroundImage: `url(${send})` }}
></div>
</form>
</div>
</div>
);
};
export default CardMediumMessage;
I think I found the solution so I would like to share it :
My mistake was that I was calling functions that were async in themselves but I didn't chain them in an async/await manner.
This is what I am talking about :
const asyncWarperSequence = async () => {
numberOfMessagesSent !== 0 && (await asyncWarperCreateDiscussionInDb());
await asyncWarperFetchDiscussionInDb();
};
useEffect(() => {
console.log("UseEffect Fired");
asyncWarperSequence();
}, [numberOfMessagesSent]);
I am trying to display the image of the breed selected by the user in this code but it is not working,
any ideas or hints as of why?
Thank you
import React, { useState, useEffect } from 'react';
import './breed-image.css';
function BreedImage () {
const [breed, selectedBreed] = useState('');
useEffect(() => {
fetchImage();
}, []);
const fetchImage = async () => {
const res = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`)
const data = await res.json();
const imageUrl = data.message
selectedBreed(imageUrl);
};
return (
<div className="image-container">
<img className="image-card" src={breed} alt="doggopicture" />
</div>
);
}
export default BreedImage;
There's some weird logic in here that doesn't make sense. breed is initialized to an empty string. Then, in your useEffect you have an empty dependencies array, which means it will be called once. Which means your API request hits https://dog.ceo/api/breed//images/random which presumably would fail (since breed was '').
Most likely you instead want:
import React, { useState, useEffect } from 'react';
import './breed-image.css';
function BreedImage () {
const [breed, setBreed] = useState('');
const [url, setUrl] = useState('');
useEffect(() => {
fetchImage();
}, [breed]);
const fetchImage = async () => {
const res = await fetch(`https://dog.ceo/api/breed/${breed}/images/random`)
const data = await res.json();
setUrl(data.message);
};
return (
<>
<DogPicker onChange={(breed) => setBreed(breed)} />
<div className="image-container">
<img className="image-card" src={url} alt="doggopicture" />
</div>
</>
);
}
export default BreedImage;
where you'd pass setBreed to some other component. Or, you could pass breed down to this component as a prop, and again use useEffect to watch for changes.
Here Below my code I would like to retrieve all data before starting the render of my component, is there any way to do that in react ? I guess it's maybe a simple code line but as I'm new in coding I still don't know all react components behavior. Thanks for your answer.
import { useState, useEffect } from "react";
import axios from "axios";
import Cookies from "js-cookie";
// import material ui
import CircularProgress from "#mui/material/CircularProgress";
import Box from "#mui/material/Box";
// import config file
import { SERVER_URL } from "../../configEnv";
const Products = ({ catList }) => {
// catList is data coming from app.js file in format Array[objects...]
console.log("catList ==>", catList);
const [isLoading, setIsLoading] = useState(true);
const [dataSku, setDataSku] = useState([]);
console.log("datasku ==>", dataSku);
const tab = [];
useEffect(() => {
// Based on the catList tab I fetch additionnal data linked with each object of catList array
catList.slice(0, 2).forEach(async (element) => {
const { data } = await axios.post(`${SERVER_URL}/products`, {
product_skus: element.product_skus,
});
// The result I receive from the call is an array of objects that I push inside the Tab variable
tab.push({ name: element.name, content: data });
setDataSku(tab);
console.log("tab ==>", tab);
setIsLoading(false);
});
}, [catList]);
return isLoading ? (
<Box sx={{ display: "flex" }}>
{console.log("there")}
<CircularProgress />
</Box>
) : (
<div className="products-container">
<div>LEFT BAR</div>
<div>
{dataSku.map((elem) => {
return (
<div>
<h2>{elem.name}</h2>
</div>
);
})}
</div>
</div>
);
};
export default Products; ```
#Jessy use your loading state to fetch data once,
In your useEffect, check for loading,
useEffect(() => {
if(loading) {
catList.slice(0, 2).forEach(async (element) => {
const { data } = await axios.post(`${SERVER_URL}/products`, {
product_skus: element.product_skus,
});
tab.push({ name: element.name, content: data });
setDataSku(tab);
console.log("tab ==>", tab);
setIsLoading(false);
});
}
}, [catList]);`
I finally managed to displayed all results by adding this condition on the isLoading
if (tab.length === catList.length) {
setIsLoading(false);
}
Many thanks guys for your insight :)
I don't understand why the second line, which reads data from the props, is not displayed as instantly as the first, i would like them to be displayed instantly
I update the state when a button is clicked, which calls api, data is coming in, the state is updating, but the second line requires an additional press to display
How to display both lines at once after a call? What's my mistake?
I'm using react hooks, and i know that required to use useEffect for re-render component, i know, that how do work asynchronous call,but i'm a little confused, how can i solve my problem, maybe i need to use 'useDeep effect' so that watching my object properties, or i don't understand at all how to use 'useEffect' in my situation, or even my api call incorrectly?
I have tried many different solution methods, for instance using Promise.all, waiting for a response and only then update the state
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./test";
ReactDOM.render(<App />, document.getElementById("root"));
app.js
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const useDataApi = (initialState) => {
const [state, setState] = useState(initialState);
const stateCopy = [...state];
const setDate = (number, value) => {
setState(() => {
stateCopy[number].date = value;
return stateCopy;
});
};
const setInfo = async () => {
stateCopy.map((item, index) =>
getFetch(item.steamId).then((res) => setDate(index, res.Date))
);
};
const getFetch = async (id) => {
if (id === "") return;
const requestID = await fetch(`https://api.covid19api.com/summary`);
const responseJSON = await requestID.json();
console.log(responseJSON);
const result = await responseJSON;
return result;
};
return { state, setState, setInfo };
};
const Children = ({ data }) => {
return (
<>
<ul>
{data.map((item) => (
<li key={item.id}>
{item.date ? item.date : "Not data"}
<br></br>
</li>
))}
</ul>
</>
);
};
const InfoUsers = ({ number, steamid, change }) => {
return (
<>
<input
value={steamid}
numb={number}
onChange={(e) => change(number, e.target.value)}
/>
</>
);
};
function App() {
const usersProfiles = [
{ date: "", id: 1 },
{ date: "", id: 2 }
];
const profiles = useDataApi(usersProfiles);
return (
<div>
<InfoUsers number={0} change={profiles.setID} />
<InfoUsers number={1} change={profiles.setID} />
<button onClick={() => profiles.setInfo()}>Get</button>
<Children data={profiles.state} loading={profiles} />
</div>
);
}
export default App;
To get the data, just click GET
In this example, completely removed useEffect, maybe i don’t understand how to use it correctly.
P.s: Sorry for bad english
You don't need stateCopy, as you have it in the callback of the setState:
const setInfo = async () => {
// we want to update the component only once
const results = await Promise.all(
state.map(item => getFetch(item.steamId))
);
// 's' is the current state
setState(s =>
results.map((res, index) => ({ ...s[index], date: res.Date })
);
};
this is my react code here I am getting react poll using API but when I start working on handalchange For POST API request I need (PollId,userId and answer) I am getting userId through { const userId = isAutheticated() && isAutheticated().user._id; } but I do not understand how can I get PollId from my all polls, please help...!
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
import "../../styles.css";
import { isAutheticated } from "../../auth/helper/index";
import { getPolls, postPoll } from "../helper/coreapicalls";
import { useParams } from "react-router-dom";
const MainPoll = () => {
const userId = isAutheticated() && isAutheticated().user._id;
const pollId = useParams();
const id = pollId._Id;
console.log(id);
const [polls, setPoll] = useState([]);
const [error, seterror] = useState(false);
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([]);
useEffect(() => {
loadPoll();
}, []);
const loadPoll = () => {
getPolls().then((data) => {
if (data.error) {
seterror(data.error);
} else {
setPoll(data);
console.log(data);
}
});
};
// Handling user vote
// Increments the votes count of answer when the user votes
const handalchange = () => {
postPoll();
console.log("hello");
};
return (
<div className="">
<div className="container my-5">
<h1 className="blog_heading my-3">Poll's of the Day</h1>
<div className="row">
{polls.reverse().map((poll, index) => (
<div className="col-lg-4 col-12 poll_border" key={index}>
<Poll
noStorage
question={poll.question}
answers={Object.keys(poll.options).map((key) => {
return {
option: key,
votes: poll.options[key].length,
};
})}
onVote={handalchange}
className="mb-2"
/>
</div>
))}
</div>
</div>
</div>
);
};
export default MainPoll;
my frontend image -
Here I have 5 polls , so I can not get PollId from useParams ...! so how can I get..?
Your component seems to represent list of polls, not any specific poll. So if you have an array of polls instead of one poll, than you have multiple ids instead of the single one.
You can get them by mapping your polls array like that:
const pollIds = polls.map((poll) => poll.id); // or any other prop that stores id