Its a movie search/finder, with pagination component.
The pagination currently doesn't work as aspected.
issue: clicking the pagination the movies are always the same.
Sorry about the codepen, I'm new to react and I'm a bit lost.
https://codepen.io/davide77/pen/xxrRdZR?editors=1010
<div className="pagination-container">
<div className="pagination-left">
<span>{total} results found</span>
</div>
<div className="pagination-right">
<div className="pagination-current">
Page {pageNo + 1} of {maxPages}
</div>
<div className="pagination-buttons">
<div className="pagination-button-wrapper">
<button onClick={onPrev} className="pagination-button">
<
</button>
</div>
</div>
<div className="pagination-buttons">
<div className="pagination-button-wrapper">
<button onClick={onNext} className="pagination-button">
>
</button>
</div>
</div>
</div>
There is a problem on your useEffect. You wrote:
useEffect(() => {
getMovies();
}, [pageNo]);
But should be:
useEffect(() => {
getMovies(keyword, pageNo);
}, [pageNo, keyword]);
Why? Because every time you change pageNo, useEffect will be fired but if you don't pass to the function the new page value, getMovies will return always with films of the first page.
Then, of course, pageNo should start from 1:
const [pageNo, setPageNo] = useState(1);
And of course you should change this div (is not necessary to do pageNo - 1)
<div className="pagination-current">
Page {pageNo} of {maxPages}
</div>
Here your codepen modified.
Just use react-bhx-pagination to do this easily passing a json or the endpoint. Everything will work
Repo: react-bhx-pagination
Example: Online Demo
Related
I am trying to utilize React Responsive Carousel. Currently, with my implementation, this code returns all objects in the array on the 1st page of the carousel (rending the point of a carousel useless). I'd like to have it so that each page of the carousel only shows one object in the array and you would need to utilize the "next" or "arrow" button via the carousel to view the next object in the array.
I tried to change profiles.map to profiles.forEach though this doesn't return any object at all.
I'm currently in a coding bootcamp, so forgive me if the code is bad altogether.
const { loading, data } = useQuery(QUERY_PROFILES);
const profiles = data?.profiles || [];
return (
<Carousel>
<div className="flex-row justify-space-between my-4">
{profiles &&
profiles.map((profile) => (
<div className="card col-12 col-xl-6" key={profile._id}>
<div className="card-body">
<h3 className="card-title">
{profile.firstName} {profile.lastName} <br />
<span className="mechLoc">
Location: {profile.location}
</span>
</h3>
<Link
className="seeProfileBtn btn btn-primary"
to={`/profiles/${profile._id}`}
>
See mechanic profile
</Link>
</div>
</div>
))}
</div>
</Carousel>
);
}
I am pulling project info from database and then based on how many projects I have, I push to an array and render. The problem is for some reason the "dropdownOptions" will not toggle between showing and hiding.
I basically just want to be able to click the dropdown arrow and have it show options like "delete" or "settings". Also not sure if I need to have a separate id for each project component created.
Code Snippets:
// Using hook to set display of dropdown to false
const [dropdownOptions, setDropdownOptions] = useState(false);
// This piece automatically renders clickable projects from database
const pList = [];
for (var i = 0; i < project_list.length; i += 1) {
var projectName = project_list[i]
pList.push (
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{setDropdownOptions(!dropdownOptions)}>
{dropdownOptions ? <ProjectDropdown/> : null}
</div>
</div>
</div>
);
};
setpList(pList);
})
// Then I simply render the component "pList"
return (
<div className="container">
<div className="projectsContainer">
<div className="projectsTitle">
Projects:
<div className="addProject" onClick={toggleShow}></div>
</div>
{pList}
<div className="message">
{message}
</div>
</div>
</div>
)
Ideal look would be this before user presses droparrow on project
And this afterwards, but only for the project clicked
You have to options to such problems:
1- make a state for each item in the list, and for each one you have a boolean that can tell if that item has its dropdown opened or not
2- make single state item, but make it a number that represents the clicked on item id, when that id matches the state you open the dropdown for that item
const [selectedItem, setSelectedItem] = useState(-1);
and in your jsx
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{()=>setSelectedItem(i)}>
{selectedItem == i ? <ProjectDropdown /> : null}
</div>
</div>
</div>
And as an advice, it's preferable to add a real id taken from your API, because the key passed to each item is so important for react, not only for showing the dropdown.
Suggested solution code
function TestSolution() {
// Using hook to set display of dropdown to false
const [selectedItem, setSelectedItem] = useState(-1);
// This piece automatically renders clickable projects from database
const pList = project_list.map((projectName, i) => (
<div className="projects" key={i}>
<div className="projectName">Name: <div className="projectText">{projectName}</div></div>
<div className="projectDate"> Date Created: <div className="projectText">{date}</div></div>
<div className="projectLabels">LabelsLeft:</div>
<div className="drop">
<div className="dropArrow" onClick{() => setSelectedItem(i)}>
{selectedItem == i ? <ProjectDropdown /> : null}
</div>
</div>
</div>
))
// Then I simply render the component "pList"
return (
<div className="container">
<div className="projectsContainer">
<div className="projectsTitle">
Projects:
<div className="addProject" onClick={toggleShow}></div>
</div>
{pList}
<div className="message">
{message}
</div>
</div>
</div>
)
}
Thinking in react is different, don't use a new state for some data if you can derive it from another state or data, that will make your life easier.
When the user clicks a payment method card it should set the values of the usestates to the respective values.
How do I assign multiple values in the onClick button function? Thanks.
<div className="col-md-auto" key={p.id}>
<div className="card mb-4 shadow-sm">
<div className="card-body">
<p className="card-text"><b>{p.card_number}</b></p>
<p className="card-text"><b>{p.expiry_date}</b></p>
<p className="card-text"><b>{p.cardholder_name}</></p>
<p className="card-text"><b>{p.sort_code}</b></p>
<p className="card-text"><b>{p.cvv_number}</b></p>
<div className="btn-group mr-2">
<button
onClick={e => setExpiry_date(p.card_number),setExpiry_Date(p.expiry_date)}>
Click me!
</button>
</div>
</div>
</div>
</div>
I think having a handler might make it a bit more readable and you might be getting the errors because of the syntax and some missing commas.
I don't know if it's a mistake, but you are doing setExpiryDate twice in your handler for both card number and expiry date. I think there should be two different values. Will you mind sharing your whole component code for more clarity?
Also, try to use camelCase for all handers, some linters might not allow snake cases and React also uses camelCase itself.
Can you see if the following works for you.
const Component = () => {
const handleButtonClick = () => {
setCardNumber(cardNumber);
setExpiryDate(expiryDate);
}
return (
<button onClick={handleButtonClick}>
Click me
</button>
);
}
Given an image gallery, where the user can click an image and the selected image will be shown in the gallery below it, how can I use useRef to replace the image in the gallery div with the selected image on click? The idea is that the images will be populating the gallery at the top from an array, so each image will presumably have the useRef applied to it?
HTML
<div class="wrapper">
<div class="row">
<div class="column">
<img src="imageurl" alt="water" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl2" alt="tree" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl3" alt="snow" style="width:100%" onClick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl4" alt="mountain" style="width:100%" onclick="myFunction(this);">
</div>
<div class="column">
<img src="imageurl5" alt="tree2" style="width:100%" onclick="myFunction(this);">
</div>
</div>
<div class="container">
<span onclick="this.parentElement.style.display='none'" class="closebtn">×</span>
<img id="expandedImg" style="width:100%">
<div id="imgtext"></div>
</div>
</div>
Function to toggle large image visibility
const myFunction = imgs => {
const imageRef = useRef();
var expandImg = document.getElementById("expandedImg");
var imgText = document.getElementById("imgtext");
expandImg.src = imgs.src;
imgText.innerHTML = imgs.alt;
expandImg.parentElement.style.display = "block";
}
The current state can be seen here: jsFiddle
As the React Docs state: Don’t Overuse Refs
Your code snippet is not what refs should be used for. In React, you should make things interactive via state changes, not by fiddling with the DOM, React is designed to do that for you.
However from that jsfiddle, it looks like you aren't even using React? I am unsure why you are using useRef at all?
UPDATE: Code fix
By using function to define your function (instead of an arrow function), it gets hoisted so your HTML elements can pick it up, and the getElementById method gets you the element reference you need.
Just replace all your js with:
var expandImg = document.getElementById("expandedImg");
var imgText = document.getElementById("imgtext");
function myFunction(imgs) {
expandImg.src = imgs.src;
imgText.innerHTML = imgs.alt;
expandImg.parentElement.style.display = "block";
}
...and your jsfiddle works.
I am trying to refactor from class based to functional. While doing so I will need to use hooks instead of setting this.state etc.. I am trying to get a FORM to open when i click a button. The button will also change from "add reply" to "submit comment" once the form opens. I am stumped. This is the best thing I could come up with... Doesnt work. in fact, it makes my "add reply" button completely disappear. Any thoughts on this? Here is the code that I have written. inside of the comment I am trying to return a component using ternary....
image of component as-is
import React, {useState} from 'react';
import FormOpen from './FormOpen';
const CommentCreated = (props) => {
const [resource, setResource] = useState([{visible: false, addReply: 'Add Reply'}]);
return (
<div className="ui threaded comments">
<h3 className="ui dividing header">Comments</h3>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src= {props.avatar} />
</a>
<div className="content">
<a href="/" className="author">{props.author}
</a>
<div className="metadata">
<span className="date">Today at 5:42PM</span>
</div>
<div className="text">{props.content}
</div>
<div className="actions">
{resource.visible ? <FormOpen /> : null}
<a className="reply" onClick={() => {setResource([{visible: true, addReply: 'Submit Comment'}]);}}>{resource.addReply}
</a>
<a className="save">Save</a>
<a className="hide">Hide</a>
</div>
</div>
</div>
</div>
);
};
export default CommentCreated;
You should replace the setResource(['Submit Comment', true]) with :
setResource([{
visible: true,
addReply: 'Submit Comment'
}])
To match the shape of your state.
EDIT:
Here is a working example based on your code.
As I can see from your example, your resource state doesn't need to be an array but simply an object.
Furthermore, as you are using hyperlinks <a />, you must use preventDefault() on your event being triggered if you don't want the page to refresh.