How to swipe card programatticaly in react using react-tinder card - reactjs

I am fetching data from the Backend and loading them in the card using react-tinder-card
Swiping works properly but unable to swipe using the buttons
I follow the documentation but still did not work
Here is the sample
Swiping gestures are working fine.
But when implement by checking documentation things did not work
Things are not working and tomorrow is my project day
import React, { useEffect, useState, useContext, useRef } from "react";
function Wink() {
const [people, setPeople] = useState([]);
const [loading, setLoading] = useState(false);
const [currentIndex, setCurrentIndex] = useState(people.length - 1)
const [lastDirection, setLastDirection] = useState()
// used for outOfFrame closure
const currentIndexRef = useRef(currentIndex)
const childRefs = useMemo(
() =>
Array(people.length)
.fill(0)
.map((i) => React.createRef()),
[]
)
const updateCurrentIndex = (val) => {
setCurrentIndex(val)
currentIndexRef.current = val
}
const canGoBack = currentIndex < people.length - 1
const canSwipe = currentIndex >= 0
// set last direction and decrease current index
const swiped = (direction, nameToDelete, index) => {
setLastDirection(direction)
updateCurrentIndex(index - 1)
}
const outOfFrame = (name, idx) => {
console.log(`${name} (${idx}) left the screen!`, currentIndexRef.current)
// handle the case in which go back is pressed before card goes outOfFrame
currentIndexRef.current >= idx && childRefs[idx].current.restoreCard()
// TODO: when quickly swipe and restore multiple times the same card,
// it happens multiple outOfFrame events are queued and the card disappear
// during latest swipes. Only the last outOfFrame event should be considered valid
}
const swipe = async (dir) => {
if (canSwipe && currentIndex < db.length) {
await childRefs[currentIndex].current.swipe(dir) // Swipe the card!
}
}
// increase current index and show card
const goBack = async () => {
if (!canGoBack) return
const newIndex = currentIndex + 1
updateCurrentIndex(newIndex)
await childRefs[newIndex].current.restoreCard()
}
useEffect(() => {
setLoading(true);
axios
.post("http://localhost:4000/api/all-profile", { email })
.then(function (response) {
setPeople(response.data);
setCurrentIndex(response.data.length);
}
}, []);
return (
<div className="DateMainDiv">
<Header />
<div className="ProfieCards">
{people.map((person) => (
<TinderCard
className="swipe"
key={person.email}
ref={childRefs[index]}
preventSwipe={swipe}
onSwipe={(dir) => swiped(dir, person.name, person.email)}
onCardLeftScreen={onCardLeftScreen}
onCardUpScreen={onCardUpScreen}
>
<div
style={{ backgroundImage: `url(${person.image})` }}
className="Winkcard"
>
<img
onLoad={handleLoad}
src={person.image}
alt="Image"
className="TinderImage"
/>
<h3>
{person.name}{" "}
<IconButton
style={{ color: "#fbab7e" }}
onClick={() => handleOpen(person.email)}
>
<PersonPinSharpIcon fontSize="large" />
{parseInt(person.dist/1000)+"KM Away"}
</IconButton>
</h3>
</div>
</TinderCard>
))}
<SwipeButtonsLeft onClick={()=>{swipe("left")}} />
<SwipeButtonsLeft onClick={()=>{goback()}} />
<SwipeButtonsLeft onClick={()=>{swipe("right")}} />
</div>
</div>
);
}
export default Wink;

Related

component doesn't render after mergin two APIS

After I request two endpoints and store it in a new state variable I'm not being able to render the component after the state changes. When i assign the state variable to the dependency array of useEffect it renders infinitely.
I tried a few things but the only way that i've being able to do to render the component after it loads has been just adding the merge state to the dependency array.
import { ChangeEvent, FC, useEffect, useState } from "react";
import spacex from "../api/spacex";
import CardGrid from "../components/CardGrid";
import Header from "../components/Header";
import Pagination from "../components/Pagination";
import SkeletonGrid from "../components/SkeletonGrid";
type Launch = {
mission_name: string;
};
const LaunchesMain: FC = () => {
const [launches, setLaunches] = useState<any>([]);
const [rockets, setRockets] = useState<any>([]);
const [merged, setMerged] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [postsPerPage, setPostsPerPage] = useState(9);
const [searchTerm, setSearchTerm] = useState("");
const [filteredResult, setFilteredResult] = useState([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const fetchRockets = async () => {
const responseRocket = await spacex.get("/rockets");
const responseLaunches = await spacex.get("/launches");
setRockets(responseRocket.data);
setLaunches(responseLaunches.data);
};
fetchRockets().then(() => {
const mergedApis = () => {
const launchesCopy: any = [...launches];
for (let i = 0; i < launches.length; i++) {
for (let j = 0; j < rockets.length; j++) {
if (launches[i].rocket.rocket_name === rockets[j].rocket_name) {
launchesCopy[i].rocket = rockets[j];
}
}
}
setMerged(launchesCopy);
setIsLoading(false);
};
mergedApis();
});
}, []);
console.log(merged);
const handleSearchChange = (event: ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
if (searchTerm.length === 0) {
setFilteredResult(merged);
} else if (searchTerm.length > 0) {
const filteredData = merged.filter((launch: Launch) => {
return `${launch.mission_name}`
.toLowerCase()
.includes(searchTerm.toLowerCase());
});
setFilteredResult(filteredData);
}
};
const lastPostIndex = currentPage * postsPerPage;
const firstPostIndex = lastPostIndex - postsPerPage;
const currentPosts = merged.slice(firstPostIndex, lastPostIndex);
return (
<>
<Header />
<div className="text-white">
<div>
<input
style={{
background:
"linear-gradient(0deg, rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.05)), #121212",
}}
onChange={(event) => handleSearchChange(event)}
placeholder="Search all launches..."
value={searchTerm}
className="md:w-[26rem] w-[16rem] h-[3rem] rounded-lg mt-10 mx-5 md:mx-24 rounded-3"
/>
</div>
<div className="mx-5 md:ml-24 mt-5 opacity-40">
Total({currentPosts.length})
</div>
{isLoading ? (
<SkeletonGrid cards={postsPerPage} />
) : (
<CardGrid
postsData={currentPosts}
filteredResult={filteredResult}
searchTerm={searchTerm}
/>
)}
<Pagination
totalPosts={merged.length}
postsPerPage={postsPerPage}
setCurrentPage={setCurrentPage}
currentPage={currentPage}
/>
</div>
</>
);
};
export default LaunchesMain;
This is the code of the component. How can i solve this issue?
Since you need rockets and launches as a dependency of the useEffect, whenever they change, the useEffect is called, which calls the api, which changes, etc... However, you don't use rockets and launches states beyond merging them, and then you use the merged state.
So you don't have to store rockets and launches in the state. Use Promise.all() to get both data arrays in to .then() block, merge them, and store only the merged state:
useEffect(() => {
const fetchRockets = () => Promise.all(
spacex.get("/rockets"),
spacex.get("/launches")
])
fetchRockets()
.then(([responseRocket, responseLaunches]) => {
const rockets = responseRocket.data;
const launches = responseLaunches.data;
for (let i = 0; i < launches.length; i++) {
for (let j = 0; j < rockets.length; j++) {
if (launches[i].rocket.rocket_name === rockets[j].rocket_name) {
launches[i].rocket = rockets[j];
}
}
}
setMerged(launches);
setIsLoading(false);
});
}, []);

Why is the min state getting updated 2 times instead of only once?

Why is the min state getting updated in the multiples of two instead of just updating by one after every 59 seconds? How do I fix it?
import { useRef, useState } from "react";
export const Timer = () => {
const [second, setSecond] = useState(0);
const [min, setMin] = useState(0);
const watch = useRef(null);
const startTimer = () => {
watch.current = setInterval(() => {
setSecond((value) => {
if (value === 59) {
setSecond(0);
setMin((v) => v + 1);
}
return value + 1;
});
}, 1000);
};
return (
<div>
<h1>
{min}:{second}{" "}
</h1>
<button onClick={startTimer}>Start</button>
<button onClick={() => clearInterval(watch.current)}>Pause</button>
<button
onClick={() => {
setSecond(0);
return clearInterval(watch.current);
}}
>
Reset
</button>
</div>
);
};
This is the component as a whole. I am new to react so please help.

React listen to child's state from parent

Damn, two days, two noob questions, sorry guys.
Yesterday, I spent the whole afternoon reading the docs but my fart-ey brain cannot process how to use react hooks to pass data from a child to a parent.
I want to create a button on my parent that can listen to his child's state to check on it and change the background color depending on its value.
Thing is, the child component is mapping some stuff so I cannot create a button (otherwhise it would be rendered multiple times and not only once like I want).
I've thought about moving all the data to my parent component but I cannot understand how since I'm fairly new to React and it's been only two months of learning how to code for me basically.
I will now provide the code for the parent and the child component.
The parent :
import React from "react";
import Quizz from "./components/Quizz";
export default function App() {
const [quizz, setQuizz] = React.useState([]);
React.useEffect(() => {
async function getData() {
const res = await fetch(
"https://opentdb.com/api.php?amount=5&category=27&type=multiple"
);
const data = await res.json();
setQuizz(data.results)
}
getData();
}, []);
function checkOnChild(){ /* <== the function I'd like to use to check on my Quizz component's "activeAnswer" state */
console.log(quizz);
}
const cards = quizz.map((item, key) => {
return <Quizz {...item} key={key}/>;
});
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button> /* <== the button that will use the function */
</div>
);
}
and the child :
import React from "react";
import { useRef } from "react";
export default function Quizz(props) {
const [activeAnswer, setActiveAnswer] = React.useState('');/* <== the state I'd like to check on from my parent component */
function toggle(answer) {
setActiveAnswer(answer);
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
let answers = props.incorrect_answers;
const ref = useRef(false);
if (!ref.current) {
answers.push(props.correct_answer);
shuffleArray(answers);
ref.current = true;
}
const answerDiv = answers.map((answer, key) => (
<div key={key} className="individuals" onClick={()=> toggle(answer)}
style={{background: answer == activeAnswer ? "#D6DBF5" : "transparent" }}>
{answer}
</div>
));
console.log(answers);
console.log(activeAnswer);
console.log(props.correct_answer);
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
);
}
I'm really sorry If it seems dumb or if I'm making forbidden things lmao, but thanks in advance for your help guys!
This should get you a bit further I think.
export default function App() {
const [quizData, setQuizData] = useState([])
const [quizState, setQuizState] = useState({})
useEffect(() => {
async function getData() {
const res = await fetch('https://opentdb.com/api.php?amount=5&category=27&type=multiple')
const data = await res.json()
const results = data.results
setQuizData(results)
setQuizState(results.reduce((acc, curr) => ({ ...acc, [curr.question]: '' }), {}))
}
getData()
}, [])
function checkOnChild() {
console.log(quizState)
}
const cards = quizData.map((item) => {
return <Quizz {...item} key={item.question} quizState={quizState} setQuizState={setQuizState} />
})
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button>
</div>
)
}
export default function Quizz(props) {
function handleOnClick(answer) {
props.setQuizState(prevState => ({
...prevState,
[props.question]: answer,
}))
}
const answers = useMemo(() => {
const arr = [...props.incorrect_answers, props.correct_answer]
return shuffleArray(arr)
}, [props.incorrect_answers, props.correct_answer])
const answerDiv = answers.map((answer) => (
<div
className="individuals"
key={answer}
onClick={() => handleOnClick(answer)}
style={{ background: answer == props.quizState[props.question] ? '#D6DBF5' : 'transparent' }}
>
{answer}
</div>
))
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
)
}

How to load more images from the API by 10 onclick and limit to 50?

I'm having a hard time understanding how to add the Button with {handleShowMoreImages} to App.js instead of Album.js
I'm using an API for fetching Images from this component Album.js
const Album = () => {
const max = 50;
// Use State for Images.
const [images, setImages] = useState([]);
const [limit, setLimit] = useState(10);
// API
const api = "https://jsonplaceholder.typicode.com/photos";
// On Mount Call API & Filter Data
useEffect(() => {
fetchImages();
}, [limit, images]);
// Fetch Data
const fetchImages = () => {
// Call the API
fetch(api)
.then((res) => res.json())
.then((data) => {
// Filter to Even albumId only
const filterData = data.filter((x) => x.albumId % 2 === 0);
})
.catch((err) => console.log(err));
}
const handleShowMoreImages = () => {
// I'm getting an error here
if(limit <= max) {
let limit = limit + 10;
setLimit(limit)
}
};
return (
<div>
<Grid container spacing={6} justifyContent="center" alignItems="center">
{images.slice(0, limit).map((album) => (
<Grid item key={album.id} xs={12} sm={6} md={4}>
<img className="albumImg" src={album.url} alt={album.title} />
</Grid>
))}
<Button onClick={handleShowMoreImages} variant="contained">
Load More
</Button>
</Grid>
</div>
);
};
export default Album;
How can I add the button in App.js and when it's clicked it renders 10 more images until it's 50?
const App = () => {
return (
<>
<NavBar />
<Album />
// How to add the button here and render from Album.js
</>
);
};
export default App;
Thanks ahead for your help.
Final Output:
const handleShowMoreImages = () => {
if (limit <= max) {
// let limit = limit + 10; <= you are initializing limit state one more time here,
setLimit(limit+10);
//^^^^^^^^^^^^^^^^^^--------- do this instead
}
};
Full Example:
export default function App() {
const max = 50;
// Use State for Images.
const [images, setImages] = useState([]);
const [limit, setLimit] = useState(10);
// API
const api = "https://jsonplaceholder.typicode.com/photos";
// On Mount Call API & Filter Data
useEffect(() => {
fetchImages();
console.log(limit);
}, [limit]);
// Fetch Data
const fetchImages = () => {
// Call the API
fetch(api)
.then(res => res.json())
.then(data => {
// Filter to Even albumId only
const filterData = data.filter(x => x.albumId % 2 === 0);
setImages(filterData);
})
.catch(err => console.log(err));
};
const handleShowMoreImages = () => {
// I'm getting an error her
if (limit <= max) {
setLimit(limit + 10);
}
};
return (
<div>
{images.slice(0, limit).map(album => (
<img className="albumImg" src={album.url} alt={album.title} />
))}
<button
disabled={limit >= max}
onClick={handleShowMoreImages}
variant="contained"
>
Load More
</button>
</div>
);
}
Full working example: Stackblitz

Memorize fetched results in component

I'm working on a component that adds images to items. You can either upload your own image or pick an image, loaded from an API based on the name of the item.
Here is the root component:
const AddMedia = (props) => {
const [currentTab, setCurrentTab] = useState(0);
const [itemName, setItemName] = useState(props.itemName);
return (
<div>
<Tabs
value={currentTab}
onChange={() => setCurrentTab(currentTab === 0 ? 1 : 0)}
/>
<div hidden={currentTab !== 0}>
<FileUpload />
</div>
<div hidden={currentTab !== 1}>
{currentTab === 1 && <ImagePicker searchTerm={itemName} />}
</div>
</div>
);
};
And here is the <ImagePicker />:
import React, { useState, useEffect } from "react";
function ImagePicker({ searchTerm, ...props }) {
const [photos, setPhotos] = useState([]);
const searchForImages = async (keyword) => {
const images = await api.GetImagesByKeyword(keyword);
return images;
};
useEffect(() => {
const result = searchForImages(searchTerm);
setPhotos(result);
}, []);
return (
<>
{photos.map(({ urls: { small } }, j) => (
<img alt={j} src={small} className={classes.img} />
))}
</>
);
}
const areSearchTermsEqual = (prev, next) => {
return prev.searchTerm === next.searchTerm;
};
const MemorizedImagePicker = React.memo(ImagePicker, areSearchTermsEqual);
export default MemorizedImagePicker;
What I'm struggling with is getting the component to not fetch the results again if the searchTerm hasn't changed. For example, when the component loads it's on tab 0 (upload image), you switch to tab 1 (pick an image) and it fetches the results for searchTerm, then you switch to 0 and again to 1 and it fetches them again, although the searchTerm hasn't changed. As you can see, I tried using React.memo but to no avail. Also, I added the currentTab === 1 to stop it from fetching the photos when the root component renders and fetch them only if the active tab is 1.
You should add the searchTerm as dependency of the useEffect so that it will not fetch again if searchTerm hasn't change:
useEffect(() => {
const result = searchForImages(searchTerm);
setPhotos(result);
}, [searchTerm]);
Additional information, if you are using eslint to lint your code, you can use the react-hooks/exhaustive-deps rule to avoid this kind of mistake.

Resources