Why am I unable to target a mapped out <li> from the button mapped out with it with React? - javascript

I am in the process of learning react and am working on a pretty simple food ordering app that maps through a list of meals in a component named AvailableMeals and would allow a user to hit the add to cart button on each mapped out item to add it to cart.
Right now though, I am unable to get my add button to target the LI that it is connected to. I have tried putting the targeting function in multiple places and tried to target different things (this, this.name, e.target.value, etc) but everything still comes up with undefined or throws an unable to read undefined error. I have even tried to use useContext, but still nothing. I will add the components that are relevant to this below.
My component tree is MealForm -> MealItem -> MealItemForm -> Input and will paste the markup here in that order.
Thanks in advance for any help.
const MealForm =(props) => {
return (
<ul className='mealForm'>
<MealItem meals={AvailableMeals} />
</ul>
)
}
const MealItem =(props) => {
// const mealsCtx = useContext(AvailableMeals);
const [cart, setCart] = useState(null)
const [curMeal, setCurMeal] = useState(null)
const addToCartHandler = (e, props) => {
e.preventDefault();
console.log(this); //This is what I am using to try and target
}
if ( props.meals.length === 0) {
return( <h2>No meals found!!</h2>)
} else{
return (
<Card >
{AvailableMeals.map((meal)=> {
return<>
<ul className='mealItem'>
<div className='mealItem-info'>
<li>{meal.name}</li>
<li>{meal.description}</li>
<li>{meal.price}</li>
<MealItemForm
id={meal.id}
key={meal.id}
name={meal.name}
description={meal.description}
onSub={addToCartHandler}
/>
</div>
</ul>
</>
})};
</Card>
)
}
};
const MealItemForm = (props) => {
return(
<form onSubmit={props.onSub}>
<Input label="Amount " input={{
id: 'amount_' + props.id,
type: 'number',
min: '1',
max: '10',
step: '1',
defaultValue: '1'
}}
/>
<button> + Add</button>
</form>
)
}
const Input = (props) => {
return (
<div >
<label htmlFor={props.input.id}>{props.label}</label>
<input {...props.input}/>
</div>
)
}

One is able to use stack-snippets to provide a working demo of one's code. This answer addresses the OP's question in trying to identify which meal's click event was invoked.
Changes significant to the question:
On the component: MealItemForm, changed below:
<form name={props.name} onSubmit={props.onSub}>
On the component: MealItem, changed addToCartHandler method:
console.log('mean-name corresponding to the clicked-item: ', e.target.name);
Impact observed:
The console.log properly displays the meal.name corresponding to the item's whose button was clicked.
Code Snippet
const {useState} = React;
const Input = (props) => {
return (
<div >
<label htmlFor={props.input.id}>{props.label}</label>
<input {...props.input}/>
</div>
)
};
const MealItemForm = (props) => {
return(
<form name={props.name} onSubmit={props.onSub}>
<Input label="Amount " input={{
id: 'amount_' + props.id,
type: 'number',
min: '1',
max: '10',
step: '1',
defaultValue: '1'
}}
/>
<button> + Add</button>
</form>
)
};
const MealItem = (props) => {
const [cart, setCart] = useState(null);
const [curMeal, setCurMeal] = useState(null);
const addToCartHandler = (e, props) => {
e.preventDefault();
console.log('mean-name corresponding to the clicked-item: ', e.target.name);
};
if ( props.meals.length === 0) {
return (<h2>No meals found!!</h2>)
} else {
return (
<div>
{
props.meals.map(meal => {
return (
<div>
<ul className='mealItem'>
<div className='mealItem-info'>
<li>{meal.name}</li>
<li>{meal.description}</li>
<li>{meal.price}</li>
<MealItemForm
id={meal.id}
key={meal.id}
name={meal.name}
description={meal.description}
onSub={addToCartHandler}
/>
</div>
</ul>
</div>
)
})
}
</div>
)
}
};
const AvailableMeals = [
{name: 'name 0', description: 'description 0', price: 'price 0', id: '0' },
{name: 'name 1', description: 'description 1', price: 'price 1', id: '1' },
{name: 'name 2', description: 'description 2', price: 'price 2', id: '2' },
{name: 'name 3', description: 'description 3', price: 'price 3', id: '3' },
{name: 'name 4', description: 'description 4', price: 'price 4', id: '4' }
];
const MealForm =(props) => {
return (
<ul className='mealForm'>
<MealItem meals={AvailableMeals} />
</ul>
)
};
ReactDOM.render(
<div>
DEMO
<MealForm />
</div>,
document.getElementById("rd")
);
.mealItem-info {
display: flex; border: 2px solid grey; width: fit-content;
align-items: center;
justify-content: space-around;
margin: 5px;
}
.mealItem-info>li { padding: 20px; margin-left: 25px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>

Related

in a map function, i want to set a state which let me change the background color when i click on a div

So here's my problem, i map some data i receive from the back, it returns a group of div, i would like to be able to click a div, change his color background and use it in total price (they're options you can choose).
i tried to put a state "clicked" which set true on click, but the state is on all element ans not the only one i just clicked. After if my state is true, i change the background color and add it to the total price (calculated in the modal in details)
<p className="title-config">Configuration</p>
{data &&
data.additionalCharges.map((charges, index) => {
// console.log("charges.map", charges);
return (
<div
className={
clicked === true ? "clicked-config" : "unclicked-config"
}
key={index}
onClick={() => setClicked(true)}
>
<p>{charges.title}</p>
<p>{charges.description}</p>
<p>
{charges.price.amount} {location.state.price.currency}
</p>
</div>
);
})}
</div>
<div className="colonne2-config">
<div>
<span> Total {location.state.total}</span>
<span>{location.state.price.amount}</span>
</div>
<div>
<div onClick={() => setShowModal(true)}>Voir les details du prix</div>
<Modal
isOpen={showModal}
onRequestClose={() => setShowModal(false)}
style={{
overlay: {
backgroundColor: "lightgrey",
backgroundOpacity: "50%",
},
}}
>
<h1>Details du prix</h1>
<button onClick={() => setShowModal(false)}> X </button>
</Modal>
</div>
Here is a working example to achieve the desired objective:
Code Snippet
const {useState} = React;
const SomeComponent = ({data, ...props}) => {
// the clicked is being used to achieve two goals
// 1. track which item is clicked (ie, selected)
// 2. update the total-price by adding / subtracting the clicked item's price
// NOTE: This is not a good approach to employ in general. Please avoid.
// Instead, use a separate variable to calculate the total-price.
const [clicked, setClicked] = useState({total: 0});
const getClass = idx => (`item ${clicked[idx] ? 'selected' : 'unselected'}`);
return (
<div>
<h4>List of Items</h4>
{
data && Array.isArray(data) && data.map(
({title, description, amount}, idx) => (
<div
key={idx}
onClick={() => setClicked(prev => ({
...prev,
total: (
prev[idx] ? prev.total - +amount : prev.total + +amount
),
[idx]: !prev[idx]
}))}
class={getClass(idx)}
>
{title}   {description}   {amount}
</div>
)
)
}
<br/>
Total Price: {clicked.total}
</div>
);
};
const rawData = [
{title: 'Title 00', description: 'Description 00', amount: '100'},
{title: 'Title 01', description: 'Description 01', amount: '110'},
{title: 'Title 02', description: 'Description 02', amount: '120'},
{title: 'Title 03', description: 'Description 03', amount: '130'},
{title: 'Title 04', description: 'Description 04', amount: '140'},
{title: 'Title 05', description: 'Description 05', amount: '150'},
{title: 'Title 06', description: 'Description 06', amount: '160'}
];
ReactDOM.render(
<div>
<h2>DEMO</h2>
<SomeComponent data={rawData}/>
</div>,
document.getElementById('reactdiv')
);
.item {
border: 2px solid black;
margin-bottom: 10px;
padding: 2px 15px;
cursor: default;
width: fit-content;
}
.unselected { background-color: #EEEEFF; }
.selected { background-color: #6666AA; color: white}
<div id='reactdiv'/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
Explanation
The clicked needs to be a data-structure that can track which of the items rendered are clicked (ie, selected) and which are not.
In this snippet, it is set as an object
For simplicity of the demo, the same clicked object serves a secondary purpose of holding the total price.
When user clicks on any item, it's background color changes (using getClass method)
And, the price of the item is added to or removed from total
Overall - this is a fairly simple, straight-forward code snippet.
As per my understanding of the question what you can do is, add another state called divIndex and change the condition to clicked && divIndex === index or you can just remove the clicked state and only use the divIndex state also like divIndex === index.
so i arrived to some kind of solution but it's not perfect, in my newOption, it does not remove the element i clicked on sometimes... it works perfect to add an element in the tab though...
here what i did.
const [selectedOption, setSelectedOption] = useState([]);
const [isSelected, setIsSelected] = useState(false);
const handleConfigClick = (charges, index) => {
const newOption = [...selectedOption];
// Est-ce que l'option est déjà présente ?
// console.log("index", index);
const exist = newOption.find((elem) => elem.id === charges.id);
// console.log("L'élément trouvé ====> ", exist);
if (exist) {
newOption.splice(index, 1);
setIsSelected(false);
console.log("exist");
} else {
console.log("existe pas");
setIsSelected(true);
newOption.push(charges);
}
console.log("newoption", newOption);
setSelectedOption(newOption);
};
it's probably not opti but it's close to my result, just need to resolve the case when the option does not leave the tab on click

How to change specific item button name by clicking the button in ReactJs?

I have multiple item cards on a page, I want to change only a specific item/card button " Add Favt" to "Remove Favt" when the user clicks on the "Add Favt" button. But in my case, all of the card button names change when clicked on only one card button.
Here is my approach:
const Listitem = ({ posts }) => {
const [btn, setBtn] = useState('Add Favt');
var arr = [];
const click = (index) => {
arr.push(posts[index]);
console.log(arr);
localStorage.setItem('items', JSON.stringify({ arr }));
if (btn === 'Add Favt') {
setBtn('Remove Favt');
} else {
setBtn('Add Favt');
}
};
return (
<div className="fav-content">
<ul className="card">
{posts.map((item, index) => {
console.log(item._id);
return (
<li key={item._id}>
<button onClick={() => click(index)}>{btn}</button>
<div className="post">
<h1>Name: {item.name}</h1>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
</li>
);
})}
</ul>
</div>
);
};
How to solve this problem?
This may be one possible solution to achieve the desired objective.
Code Snippet
Please view the snippet in Full Page
const {useState} = React;
const Listitem = ({posts, ...props}) => {
// track whether each card is is favorite or not
const [isFavorite, setIsFavorite] = useState({});
// when button clicked, flip card from favorite
const handleClick = e => {
const id = e.target.id;
setIsFavorite(prev => ({
...prev,
[id]: !prev[id]
}))
};
return (
<div className="fav-content">
List of favorites: {
posts
.filter(({_id}) => [_id] in isFavorite && isFavorite[_id])
.map(({name}) => name)
.join()
}
<ul className="card">
{posts.map(item => (
<li key={item._id}>
<button
id={item._id}
onClick={handleClick}
>
{
isFavorite[item._id]
? 'Remove Fav'
: 'Add Fav'
} {item.name}
</button>
<div className="post">
<h4>Name: {item.name}</h4>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
</li>
)
)}
</ul>
</div>
);
};
const itemsList = [
{_id: 1, name: 'item 1', bio: 'bio 1', link: 'link 1'},
{_id: 2, name: 'item 2', bio: 'bio 2', link: 'link 2'},
{_id: 3, name: 'item 3', bio: 'bio 3', link: 'link 3'},
{_id: 4, name: 'item 4', bio: 'bio 4', link: 'link 4'},
];
ReactDOM.render(
<div>
DEMO
<Listitem posts={[...itemsList]}/>
</div>,
document.getElementById("rd")
);
.fav-content { width: fit-content; padding: 5px 15px; }
.card { background-color: #DDDDFF; margin: 5px 15px; }
.post { background-color: #FFFFDD; margin: 5px 15px; }
<div id="rd" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
Explanation
Inline comments have been provided in the above code snippet.
You are using btn state variable for each button. setting btn in state will reflect in all of them. Make a separate component for this.
<button onClick={() => click(index)}>{btn}</button>
<div className="post">
<h1>Name: {item.name}</h1>
<p>Bio: {item.bio}</p>
<a href={item.link}>Link: {item.link}</a>
</div>
Maintain a local state in new component for status of individual item.

React JS Filter Methods: Hiding table columns using array filters and state

This is the code I am trying to rebuild using functional component, but my arrays do not behave correctly.
EXPECTED RESULT: https://stackblitz.com/edit/antd-showhidecolumns
My forked functional component version:
MY WORK https://stackblitz.com/edit/antd-showhidecolumns-rdyc8h
Main issue here is I am not able to show/hide column cells, I am not sure why my array is different when I use the same method as the original code.
My code:
const onChange = (e) => {
let { checkedColumns } = colmenu;
if (e.target.checked) {
checkedColumns = checkedColumns.filter((id) => {
return id !== e.target.id;
});
console.log('if checked columns is', checkedColumns);
} else if (!e.target.checked) {
checkedColumns.push(e.target.id);
console.log('elseif checked columns', checkedColumns);
}
const filtered = checkedColumns.filter((el) => {
return el.dataIndex !== checkedColumns.el;
});
console.log('filtered items', filtered);
setColmenu({ ...colmenu, columns: filtered });
};
working version from the old code (class component)
onChange = (e) => {
var checkedColumns = this.state.checkedColumns
if(e.target.checked){
checkedColumns = checkedColumns.filter(id => {return id !== e.target.id})
}
else if(!e.target.checked){
checkedColumns.push(e.target.id)
}
var filtered = this.state.initialColumns;
for(var i =0;i< checkedColumns.length; i++)
filtered = filtered.filter(el => {return el.dataIndex !== checkedColumns[i]})
this.setState({columns: filtered, checkedColumns: checkedColumns})
}
Something really went wrong with your code (or homework i guess?)
Please have a look at least at the docs for React.useState to set some basics.
First you should init your initalColumns and later you should filter on them.
Additional i init the checkColumns with the correct values and changed the wrong logic for changing them.
Have a look how the filtering is done via Array.includes maybe someone will ask for this ;-)
Another point is that you may split the state object in separate primitive states.
Nevertheless here is a working stackblitz and the depending code.
import React from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import './index.css';
import { Table, Button, Dropdown, Menu, Checkbox } from 'antd';
const App = () => {
const columns = [
{
title: 'Description',
dataIndex: 'description',
},
{
title: 'Employees',
dataIndex: 'employees',
},
];
const [colmenu, setColmenu] = React.useState({
value: false,
checkedColumns: ['description', 'employees'],
visibleMenuSettings: false,
columns,
initialColumns: columns,
});
const onChange = (e) => {
let { checkedColumns, columns, initialColumns } = colmenu;
if (!e.target.checked) {
checkedColumns = checkedColumns.filter((id) => {
return id !== e.target.id;
});
console.log('if checked columns is', checkedColumns);
} else if (e.target.checked) {
checkedColumns.push(e.target.id);
console.log('elseif checked columns', checkedColumns);
}
console.log(columns);
columns = initialColumns.filter((col) =>
checkedColumns.includes(col.dataIndex)
);
setColmenu({ ...colmenu, columns, checkedColumns });
};
const handleVisibleChange = (flag) => {
setColmenu({ ...colmenu, visibleMenuSettings: flag });
};
const menu = (
<Menu>
<Menu.ItemGroup title="Columns">
<Menu.Item key="0">
<Checkbox id="description" onChange={onChange} defaultChecked>
Description
</Checkbox>
</Menu.Item>
<Menu.Item key="1">
<Checkbox id="employees" onChange={onChange} defaultChecked>
Employees
</Checkbox>
</Menu.Item>
</Menu.ItemGroup>
</Menu>
);
const dataSource = [
{
key: '1',
description: 'Holiday 1',
employees: '79',
},
{
key: '2',
description: 'Holiday 2',
employees: '12',
},
{
key: '3',
description: 'Holiday 3',
employees: '0',
},
];
return (
<div>
<div className="row">
<div className="col-12 mb-3 d-flex justify-content-end align-items-center">
<Dropdown
overlay={menu}
onVisibleChange={handleVisibleChange}
visible={colmenu.visibleMenuSettings}
>
<Button>Show/Hide Columns</Button>
</Dropdown>
</div>
</div>
<div className="row">
<div className="col-12">
<Table
columns={colmenu.columns}
dataSource={dataSource}
size="small"
pagination={{
pageSizeOptions: ['20', '50'],
showSizeChanger: true,
}}
/>
</div>
</div>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('container'));

React hook page throws 'filter' of undefined (anonymous) exception and unable to perform search

I have implemented a React hookhome page where I have got list of players displaying, I have added an input search to search the players based on the player name or Position, but it throws below exception,could someone please advise the cause of exception;
TypeError: Cannot read property 'filter' of undefined (anonymous
function) C:/Project1/soccerpep/src/components/Home.js:18 15 |
setSearchTerm(event.target.value); 16 | }; 17 | React.useEffect(()
=> {
18 | const results = playerList.name.filter(player =>
| ^ 19 | player.toLowerCase().includes(searchTerm) 20 | ); 21 | setSearchResults(results);
The react hook page follows below:
import React, { useEffect, useState } from "react";
import { Link } from 'react-router-dom';
var playerList = [
{ image: '/images/person.png', name: 'Player 1', position: "Forward" },
{ image: '/images/person.png', name: 'Player 2', position: "Defense" },
{ image: '/images/person.png', name: 'Player 3', position: "Mid Fielder" },
{ image: '/images/person.png', name: 'Player 4', position: "Forward" }
];
const Home = () => {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchResults, setSearchResults] = React.useState([]);
const handleChange = event => {
setSearchTerm(event.target.value);
};
useEffect(() => {
const results = playerList.name.filter(player =>
player.toLowerCase().includes(searchTerm)
);
setSearchResults(results);
}, [searchTerm]);
return (
<div className="App">
<div className="wrapper">
<div className="playerList_header">
<h1>Players</h1>
<label>
<div className="playerSearch_Home">
<div className="playerSearch_Icon">
<img src="/images/search-image-player.jpg"></img>
</div>
<input type="text" className="playerSearch_Home_Input" placeholder="Search players..." value={searchTerm} onChange={handleChange}/>
</div>
</label>
</div>
<div className="playerList_home_page">
<div className="grid-container">
{
playerList.map(player => {
return (
<div className="grid-item">
<div>
<img className="playerProfilePic_home_tile" key={player.image} src={player.image}></img>
</div>
<div className="playerProfile_grid_border">
<h3 key={player.name}>{player.name}</h3>
<span className="playerPosition_home_tile" key={player.position}>{player.position}</span>
</div>
</div>
);
})
},
{
searchResults.map(player => {
return (
<div className="grid-item">
<div>
<img className="playerProfilePic_home_tile" key={player.image} src={player.image}></img>
</div>
<div className="playerProfile_grid_border">
<h3 key={player.name}>{player.name}</h3>
<span className="playerPosition_home_tile" key={player.position}>{player.position}</span>
</div>
</div>
);
})
}
</div>
</div>
</div>
</div>
);
}
export default Home;
playerList is an array, not an object, it doesn't have property name:
const results = playerList.filter(player =>
player.name.toLowerCase().includes(searchTerm) || player.position.toLowerCase().includes(searchTerm)
);
.filter() can be used only on arrays, you tried to run on name property which does not exist.
Instead you can use as the following:
var playerList = [
{ image: '/images/person.png', name: 'Player 1', position: "Forward" },
{ image: '/images/person.png', name: 'Player 2', position: "Defense" },
{ image: '/images/person.png', name: 'Player 3', position: "Mid Fielder" },
{ image: '/images/person.png', name: 'Player 4', position: "Forward" }
];
const searchTerm = 'Player 2';
const results = playerList.filter(p => p.name.toLowerCase() === searchTerm.toLowerCase());
console.log(results);
I hope this explains!

Implementing pagination in React that displays 15 items per page from a JSON file

I am having issues on even trying to get started with doing pagination without the use of any packages. I am pulling data from a JSON file that contains about 30-32 quotes. I need 15 quotes per page to be displayed and have no idea how to even do that using React. So far what I have is all the quotes being displayed by default. I have three buttons, each filters through the JSON to provide quotes by the theme of the quote which is displayed by the button. This is how far I got:
class App extends Component {
constructor(props) {
super(props);
this.state ={
results: quotes,
search: ""
}
}
gameFilterClick = event => {
event.preventDefault();
const games = [];
for(let i = 0; i < quotes.length; i++){
if (quotes[i].theme === "games"){
games.push(quotes[i])
}
}
this.setState({results: games})
}
movieFilterClick = event => {
event.preventDefault();
console.log('blah!!')
const movies = [];
for(let i =0; i < quotes.length; i++){
if(quotes[i].theme === 'movies'){
movies.push(quotes[i])
}
}
this.setState({results: movies})
}
allButtonClick = event => {
this.setState({results: quotes})
}
quoteSearch = query => {
let search = quotes.map
}
render() {
return (
<div className="App">
<h1>Quotes</h1>
<Search />
<div id='buttons'>
Filters:
<button onClick={this.allButtonClick}>All Quotes</button>
<button onClick={this.gameFilterClick}>Games</button>
<button onClick={this.movieFilterClick}>Movies</button>
</div>
<div id='resultsDiv'>
<Results
results={this.state.results}
/>
</div>
</div>
);
}
}
export default App;
I would recommend using react-bootstrap for this. You'll need to install two packages (they use to come in one, but now pagination package is separated):
react-bootstrap-table-next
react-bootstrap-table2-paginator
So, let's install them:
npm i --save react-bootstrap-table-next
npm i react-bootstrap-table2-paginator
And here goes a simple example of implementation:
import BootstrapTable from 'react-bootstrap-table-next';
import paginationFactory from 'react-bootstrap-table2-paginator';
// Let's imagine this is your JSON data
const yourJsonData = [{id: 1, author: "David Goggins", quote: "Life goes on"},
{ id: 2, author: "Robert Green", quote: "yes it does"}]:
// Here we define your columns
const columns = [{
dataField: 'author',
text: 'AUTHOR'
}, {
dataField: 'quote',
text: 'QUOTE'
}];
// Give it an option to show all quotes
let allQuotes = Number(yourJsonData.length);
// Set all of the major pagination options. You can reduce them if you want less
const options = {
paginationSize: 15,
pageStartIndex: 0,
firstPageText: 'First',
prePageText: 'Back',
nextPageText: 'Next',
lastPageText: 'Last',
nextPageTitle: 'First page',
prePageTitle: 'Pre page',
firstPageTitle: 'Next page',
lastPageTitle: 'Last page',
sizePerPageList: [{
text: 'show 15', value: 15
}, {
text: 'show 30', value: 30
}, {
text: 'Show all', value: allQuotes
}]
};
... and then somewhere later in your code where you want to display the table with pagination you just insert this:
<BootstrapTable
keyField='rowNumber'
data={ yourJsonData }
columns={ columns }
pagination={ paginationFactory(options) } />
I hope this solves your problem.
I've simplified your filtering logic and added client side pagination. Check out this simple working example (i've set item per page to 3, you can add more data and change it to 15 const QUOTES_PER_PAGE = <number of quotes per page>;)
const QUOTES_PER_PAGE = 3;
const Quote = ({text}) => <li>{text}</li>;
const Pagination = ({pages, goTo}) => (
<div>
{pages.map((p, i) => (
<button key={i} onClick={goTo} value={i}>{i+1}</button>
))}
</div>
)
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 0,
pagedQuoutes: this.divideQuoutesIntoPages(props.quotes)
};
}
divideQuoutesIntoPages = (quotes => {
const pagedQuotes = [];
[...Array(Math.ceil(quotes.length/QUOTES_PER_PAGE))].forEach((q, i) => {
pagedQuotes.push(quotes.slice(0 + QUOTES_PER_PAGE*i, QUOTES_PER_PAGE + QUOTES_PER_PAGE*i))
})
return pagedQuotes;
})
filterQuoutes = (evt) => {
const filterValue = evt.target.value;
const filteredQuoutes = this.props.quotes.filter(q => !filterValue || q.theme === filterValue);
this.setState({
pagedQuoutes: this.divideQuoutesIntoPages(filteredQuoutes)
})
}
goToPage = (evt) => {
this.setState({
page: evt.target.value
})
}
render() {
return (
<div>
<h1>Quotes</h1>
<div>
Filters:
<button onClick={this.filterQuoutes}>All Quotes</button>
<button onClick={this.filterQuoutes} value="games">Games</button>
<button onClick={this.filterQuoutes} value="movies">Movies</button>
</div>
{this.state.pagedQuoutes[this.state.page]
.map(q => (
<ul>
<Quote {...q} />
</ul>
))}
<Pagination pages={this.state.pagedQuoutes} goTo={this.goToPage} />
</div>
);
}
}
const exampleQuotes = [{
theme: 'games',
text: 'games q1'
}, {
theme: 'games',
text: 'games q2'
}, {
theme: 'games',
text: 'games q3'
}, {
theme: 'games',
text: 'games q4'
}, {
theme: 'games',
text: 'games q5'
}, {
theme: 'movies',
text: 'movies q1'
}, {
theme: 'movies',
text: 'movies q2'
}, {
theme: 'movies',
text: 'movies q3'
}]
ReactDOM.render(<App quotes={exampleQuotes} />, document.getElementById("el"))
<div id="el"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories