I have a page set up to render an image from Dall-E based on user input. The resulting image is wrapped with a "close-to-click" function, but I'm wondering if there's a better way to achieve this.
I wanted to use backdrop-filter: blur as a fullscreen frame for the rendered picture, so I added it to the .response class CSS and used a funky method to "hide" this until after user input and image returned.
It feels like there's a cleaner way to do it, especially since I'd like to leverage transition to the fullscreen blur, and I know this logic is preventing it.
Any feedback is appreciated, lay it on thick!
Thanks!
const [prompt, setPrompt] = useState("");
const [imgSz, setImgSz] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [response, setResponse] = useState("");
const [**showResponse**, setShowResponse] = useState(false);
const getResponseFromDalle = async () => {
setResponse("");
console.log("Image-O-Matic Making Magic...");
setIsLoading(true);
const response = await fetch("/api/openai-dalle", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt: prompt, n: imgQty, size: imgSz }),
});
const imageUrl = await response.json();
setIsLoading(false);
setResponse(imageUrl.text);
setShowResponse(true);
**document.getElementById("dalleImage").style.display = "block";**
};
function closeResponseFromDalle() {
**document.getElementById("dalleImage").style.display = "none"**;
setResponse("");
setShowResponse(false);
}
return(
// input form //
...
// rendering image //
{isLoading ? (
<div><p className={styles.code}>Generating image...</p></div>
) : (
<div></div>
)}
{showResponse ? (
<div className={styles.response} id="dalleImage" onClick={closeResponseFromDalle}>
<p><img onClick={closeResponseFromDalle} src={response}></img></p>
<br /><p onClick={closeResponseFromDalle} className={styles.tooltip}>click to close</p>
</div>
) : (
<span></span>
)}
</Layout>
Related
This my first time implementing react infinite scrolling. I ran into difficulty where the initial array of data keep repeating.
function Details() {
const [facility, setFacility] = useState([]);
const [loading, setLoading] = useState(true);
const getFacility = useCallback(async () => {
let skip = 0;
setLoading(true)
await Axios.get(
`...apiUrl/${wardTitle}/ward/details?limit=${10}&skip=${skip}`
).then((response) => {
const details = response.data.data;
setFacility((prev) => [...prev, ...details]);
setLoading(false)
});
}, [wardTitle]);
const handleScroll = (e) => {
const { scrollTop, scrollHeight, clientHeight} = e.currentTarget;
if(clientHeight + scrollTop + 1 >= scrollHeight ){
getFacility();
}
};
useEffect(() => {
getFacility();
}, [getFacility]);
return (
<>
{facility.map((cur) => (
<div>{curr}</div>
{loading && <h4>Loading ...</h4>
)
</>
)
}
Everything seems fine other than the data repetition. I dont know where i got it wrong in the call. I would appreciate any help.
i can see so many errors in your code check the spellings too.
You are not explaining what you really want to do?
infinite scrolling like facebook?
I have the following react code that pulls some data from an api and outputs it. In result['track_list'] I receive a list of tracks with a timestamp and in aggTrackList() I am aggregating the data into key value pair based on the day/month/year then displaying that aggregated data in a Card component I created.
import React, { useState, useEffect } from "react";
function App() {
const [error, setError] = useState(null);
const [isLoaded, setIsLoaded] = useState(false);
const [trackList, settracks] = useState([]);
const [sortby, setSortby] = useState("day");
const [sortedList, setSortedList] = useState([]);
useEffect(() => {
aggTrackList();
}, [sortby]);
const aggTrackList = () => {
setSortedList([]);
let sortedObj = {};
switch (sortby) {
case "day":
trackList.forEach((track) => {
let dayVal = new Date(track[3]).toDateString();
dayVal in sortedObj
? sortedObj[dayVal].push(track)
: (sortedObj[dayVal] = [track]);
});
setSortedList(sortedObj);
break;
case "month":
trackList.forEach((track) => {
let monthVal = new Date(track[3]).toDateString().split(" ");
let monthYear = monthVal[1] + monthVal[3];
monthYear in sortedObj
? sortedObj[monthYear].push(track)
: (sortedObj[monthYear] = [track]);
});
setSortedList(sortedObj);
break;
case "year":
trackList.forEach((track) => {
let yearVal = new Date(track[3]).toDateString().split(" ");
let year = yearVal[3];
year in sortedObj
? sortedObj[year].push(track)
: (sortedObj[year] = [track]);
});
setSortedList(sortedObj);
break;
}
};
const getUserTracks = (username) => {
fetch(`http://localhost/my/api/${username}`, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((res) => res.json())
.then(
(result) => {
settracks(result["tracks_played"]);
aggTrackList();
setIsLoaded(true);
},
(error) => {
console.log(error);
setIsLoaded(true);
setError(error);
}
);
};
return (
<div className="App">
<SortMenu
setSort={(selected) => {
setSortby(selected);
}}
/>
<UserForm onSubmit={getUserTracks} />
<div className="trackList">
{isLoaded ? (
Object.entries(sortedList).map(([day, track]) => (
<Card
className="card"
displayMode={sortby}
key={day}
timestamp={day}
content={track}
/>
))
) : (
<div>...</div>
)}
</div>
</div>
);
}
export default App;
The issue I am having is when UserForm is submitted and receives the data. The Card elements do not render unless I update the sortby state by clicking on one of the sortmenu options after the data has loaded. How can I get the data to show automatically after it has been loaded?
I'm creating this project to learn React so if something can be done better or if I am doing things wrong, please let me know.
Thanks.
Edit:
My code on codesandbox - https://codesandbox.io/s/fervent-minsky-bjko8?file=/src/App.js with sample data from my API.
You can do so in two ways:
In a blocking way using useLayoutEffect hook. Refer this.
In a non-blocking way using useEffect hook. Refer this.
1. useLayoutEffect
The thing to note here is that the function passed in the hook is executed first and the component is rendered.
Make the API call inside useLayoutEffect and then set the data once you obtain the response from the API. Where the data can initially be
const [data, setData] = useState(null)
The JSX must appropriately handle all the cases of different responses from the server.
2. useEffect
The thing to note here is that this function runs after the component has been rendered.
Make the API call inside the useEffect hook. Once the data is obtained, set the state variable accordingly.
Here your jsx can be something like
{
data === null ? (
<Loader />
) : data.length > 0 ? (
<Table data={data} />
) : (
<NoDataPlaceholder />
)
}
Here I am assuming the data is a list of objects. but appropriate conditions can be used for any other format. Here while the data is being fetched using the API call made inside useEffect, the user will see a loading animation. Once the data is obtained, the user will be shown the data. In case the data is empty, the user will be show appropriate placeholder message.
I have a use case where I am using the useState hook to increment value of the variable. Once the value of the variable is incremented then only I need to call a update function.
const [page, setPage] = useState(1);
const fetchMoreData = () => {
setPage(page+1);
updateNews();
};
So in essence I wanted it to be something like await setPage(page+1);. So that once the page is updated then only I fetch the news from the update URL page.
Due to this currently I am getting
index.js:1 Warning: Encountered two children with the same key, `https://english.jagran.com/trending/did-mars-ever-look-like-earth-heres-what-top-nasa-scientist-has-to-say-10033695`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
at div
at div
at div
at div
at InfiniteScroll (http://localhost:3000/static/js/vendors~main.chunk.js:32922:24)
at News (http://localhost:3000/static/js/main.chunk.js:775:89)
at Route (http://localhost:3000/static/js/vendors~main.chunk.js:34951:29)
at Switch (http://localhost:3000/static/js/vendors~main.chunk.js:35153:29)
at div
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:34582:30)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:34203:35)
at App
This is my component News.js currently
const News = (props)=>{
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);
const [page, setPage] = useState(1);
const [totalResults, setTotalResults] = useState(0);
const capitalizeFirstLetter = (string)=> {
return string.charAt(0).toUpperCase() + string.slice(1);
}
const updateNews = async ()=>{
props.setProgress(10);
let goToPage = page;
const url = `https://newsapi.org/v2/top-headlines?country=${props.country}&category=${props.category}&apiKey=${props.apiKey}&page=${goToPage}&pageSize=${props.pageSize}`;
props.setProgress(30);
let data = await fetch(url);
props.setProgress(50);
let parsedData = await data.json();
props.setProgress(70);
if(parsedData)
{
setArticles(articles.concat(parsedData.articles));
setLoading(false);
setPage(page);
setTotalResults(parsedData.totalResults);
}
props.setProgress(100);
}
useEffect(() => {
updateNews();
// eslint-disable-next-line
}, [])
const fetchMoreData = () => {
setPage(page+1);
updateNews();
};
return (
<>
<h3 className="text-center" style={{marginTop:'4%'}}>NewsMonkey - Top {`${capitalizeFirstLetter(props.category)}`} Headlines</h3>
{loading && <Spinner/>}
<InfiniteScroll
dataLength={articles.length}
next={fetchMoreData}
hasMore={articles.length < totalResults}
loader={<Spinner/>}
>
<div className="container">
<div className="row">
{articles.map((element)=>{
return (
<div className="col-md-4" key={element.url}>
<NewsItem title={element && element.title?element.title.slice(0, 45): ""} description={element && element.description?element.description.slice(0, 50):""}
imageUrl={element.urlToImage}
newsUrl ={element.url}
author={element.author}
date={element.publishedAt}
source={element.source.name}/>
</div>
)})}
</div>
</div>
</InfiniteScroll>
</>
)
}
export default News
I tried printing the value of goToPage in the update function and as I could see it was 1 every time.
Is there any way to resolve the error or wait for setPage.
Note : I tried the solution to the question which I was getting as suggestion to this question, but that did not work for me.
If you want to increment page property, you probably should use setPage with callback function, like this:
setPage(page => page + 1);
You can achieve desired effect by using useEffect with page in dependency array:
useEffect(() => {
updateNews();
}, [page])
Consider this example using React hooks:
function async apiRequest(id) {
return fetch('http://someurl.com/api/' + id)
}
function ParentComponent() {
[id, setId] = useState(0);
useEffect(() => {
const newId = await apiRequest(id);
setId(newId.result.id);
}, [id])
return (
<>
<p>ID is currently {id}</p>
<ChildButton id={1} handleOnClick={setId} />
</>
)
}
function ChildButton({id, handleOnClick}) {
render (
<button onClick={(e) => {handleOnClick(id)} }>Click Me</button>
)
}
Now suppose that I want to do 2 things:
Show that the request is in progress after I push the button
Make the button disappear after it's been clicked, and a successful request to the API has completed.
Assume that we can’t depend on a particular value being returned from the API.
What are the minimum steps I need to take to do this? Either I'm missing something, or it's very difficult to do this with React hooks.
don't know about best practices but you can.
const [clicked, setclicked] = useState(false);
const handleOnClick = () => {
setClicked(true)
}
<p>ID is currently {id}</p>
if (!clicked && id === 0) {
<ChildButton id={1} handleOnClick={handleOnClick} />
}
My idea is : when I click into the page, the page will send a axios request for the top 30 data and show them in the InfoCard. After that, when I scroll to the end of the page it will send a new axios request for the next 30 data and show them in the InfoCard.
I watched this tutorial and tried it myself but I still not sure how it works.
https://www.youtube.com/watch?v=NZKUirTtxcg
//TestScreen3.js
function TestScreen3() {
const [topNdata, setTopNdata] = useState(30)
const [skipNdata, setSkipNdata] = useState(0)
const { loading, ScenicSpot, hasMore } = RequestTest(topNdata, skipNdata)
return (
<div className="App">
<header className="App-header">
<Navbar NavbarTitle="Scenic Spots" />
<CityList />
{ScenicSpot.map((infoCard) => (<InfoCard key={infoCard.ID} Name={infoCard.Name} Description={infoCard.Description} Picture={infoCard.Picture.PictureUrl1} />))}
</header>
</div>
);
}
export default TestScreen3;
//RequestTest.js
export default function RequestTest(topNdata, skipNdata) {
const [ScenicSpot, setScenicSpot] = useState([])
const [hasMore, setHasMore] = useState(false)
const [loading, setLoading] = useState(true)
useEffect(() => {
setScenicSpot([])
}, [topNdata])
useEffect(() => {
setLoading(true)
axios({
method: 'GET',
url: 'https://ptx.transportdata.tw/MOTC/v2/Tourism/ScenicSpot',
params: { $top: topNdata, $skip: skipNdata },
}).then(res => {
setScenicSpot(res.data)
setHasMore(res.data.length > 0)
setLoading(false)
}).catch(err => { console.log(err) })
}, [topNdata, skipNdata])
return { loading, ScenicSpot, hasMore }
}
Though it will seem like a different thing.
But you will found a way for infinite scroll here at StackOverflow.
Read the question and my answer to this question.
Though I was using axios from my API, you will get the idea from there.