I am still a bit new to react and how it works. I have a projects.js file with a list of objects that look like this:
id: 0,
name: "Placeholder Project 1",
description: "Project description",
image: "./assets/images/placeholder-01.png"
There are 6 objects in this array. I am trying to render only 3 project objects at a time, and then include a button that will "load more" projects later. However, I am having trouble with just the rendering part. My component looks like this:
import React, { Component } from "react";
import NavTab from "./NavTab";
import { Card } from "react-bootstrap";
import { PROJECTS } from "../shared/projects";
function RenderProject({projects, projectsDisplayArray}) {
const tempArray = projectsDisplayArray;
return(
<div className="row">
{
projects.map(project => {
tempArray.indexOf(project) > -1 ? console.log('in array already') : tempArray.push(project)
console.log(tempArray.length)
if (tempArray.length >= 3){
console.log('in the if')
return (
<Card key={project.id}>
<Card.Img variant="top" src={project.image} />
<Card.Body>
<Card.Title>{project.name}</Card.Title>
<Card.Text>
{project.description}
</Card.Text>
</Card.Body>
<button className="btn align-self-center">Go somewhere</button>
</Card>
)
}
else {
return(<div>Else return div</div>)
}
})
}
</div>
)
}
export default class Projects extends Component {
constructor(props){
super(props);
this.state = {
projectsPerScreen: 3,
currentPage: 0,
projects: PROJECTS,
projectsDisplayArray: []
}
}
modifyProjectsDisplayArray = props => {
this.setState({projectsDisplayArray: [...this.state.projectsDisplayArray, props]})
}
render() {
let i = 0;
return(
<React.Fragment>
<NavTab/>
<div className="projects">
<div className="container">
<button type="button" className="btn">Click</button>
<h1>Projects: </h1>
<RenderProject projects={this.state.projects} projectsDisplayArray={this.state.projectsDisplayArray} />
<button type="button" className="btn" onClick={() => console.log(this.state.projectsDisplayArray)}>console log</button>
</div>
</div>
</React.Fragment>
)
}
}
I am very confused on how the return
method for RenderProject
is working. When I begin the mapping process, I want to add each project to an array so I can keep track of how many and what projects are being rendered. When the array length hits three, I want it to stop rendering. But whenever I do this, my line if (tempArray.length <= 3)
behaves in a way I don't expect it to. With how it is now, it won't return the <Card>
and will instead return the else <div>
for all 6 objects. But if I change the if statement to be if (tempArray.length >= 3)
it will render all 6 objects inside of the array and no else <div>s
. What should I be doing instead?
CodePudding user response:
I would try something like that, using the index parameter (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map). For sake of simplicity I will only add the part with the mapping function.
<div className="row">
{
projects.map((project, index) => {
if (index < 3){
return (
<Card key={project.id}>
<Card.Img variant="top" src={project.image} />
<Card.Body>
<Card.Title>{project.name}</Card.Title>
<Card.Text>
{project.description}
</Card.Text>
</Card.Body>
<button className="btn align-self-center">Go somewhere</button>
</Card>
)
}
else {
return (<div>Else return div</div>);
}
})
}
</div>
I would however consider using filter (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) or anyway create a subarray with the needed elements, then map over that array.
CodePudding user response:
You can use state to keep track of how many array items to render by keeping track of the index you want to slice your array at. Then use a new sliced array to map over. Then you can have a button that increases the index state by 3 every time it is clicked:
import { useState } from 'react';
const Component = () => {
const [index, setIndex] = useState(3)
const yourArray = [...]
const itemsToRender = yourArray.slice(0, index);
return (
<>
<button onClick={() => setIndex(index 3)}>load more</button>
<ul>
{ itemsToRender.map((item) => <li>{item}</li>) }
</ul>
</>
);
}
CodePudding user response:
The important thing to remember with React is that you don't add things to the DOM in the traditional way - you update state. So, in this contrived example, I've added some data to state, and I also have a count
state too.
count
is initially set to 1
and on the first render the getItems
function returns a new array of three items which you can then map
over to get your JSX.
When you click the button it calls handleClick
which sets a new count
state. A new render happens, and a new set of count * 3
data is returned (six items).
And so on. I also added a condition that when the count is greater than the number of items in your data the button gets disabled.
const { useState } = React;
// So here I'm just passing in some data
function Example({ data }) {
// Initialise the items state (from the data passed
// in as a prop - in this example), and a count state
const [ items, setItems ] = useState(data);
const [ count, setCount] = useState(1);
// Returns a new array of items determined by the count state
function getItems() {
return [ ...items.slice(0, count * 3)];
}
// Sets a new count state when the button is clicked
function handleClick() {
setCount(count 1);
}
// Determines whether to disable the button
function isDisabled() {
return count * 3 >= items.length;
}
return (
<div>
{getItems().map(item => {
return <div>{item}</div>;
})}
<button
onClick={handleClick}
disabled={isDisabled()}
>Load more
</button>
</div>
);
};
// Our data. It gets passed into the component
// as a property which then gets loaded into state
const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>