Pagination issue in React.js - reactjs

I've created Pagination component but have got issues while implementing it into my Characters component.
I'm getting prev and next buttons, but instead of page numbers I'm gettin NaN.
Please advise where is my mistake?
Is there an issue with Pagination props?
Pagination.js
import React, { useState } from "react";
export const Pagination = ({
data,
RenderComponent,
title,
pageLimit,
dataLimit,
}) => {
const [pages] = useState(Math.round(data.length / dataLimit));
const [currentPage, setCurrentPage] = useState(1);
function goToNextPage() {
setCurrentPage((page) => page + 1);
}
function goToPreviousPage() {
setCurrentPage((page) => page - 1);
}
const changePage = (event) => {
console.log(event.target);
const pageNumber = Number(event.target.textContent);
setCurrentPage(pageNumber);
};
const getPaginatedData = () => {
const startIndex = currentPage * dataLimit - dataLimit;
const endIndex = startIndex + dataLimit;
return data.slice(startIndex, endIndex);
};
const getPaginationGroup = () => {
let start = Math.floor((currentPage - 1) / pageLimit) * pageLimit;
return new Array(pageLimit).fill().map((_, idx) => start + idx + 1);
};
return (
<div>
<h1>{title}</h1>
<div>
{getPaginatedData().map((d, idx) => (
<RenderComponent key={idx} data={d} />
))}
</div>
<div className="pagination">
<button
onClick={goToPreviousPage}
className={`prev ${currentPage === 1 ? "disabled" : ""}`}
>
prev
</button>
{/* show page numbers */}
{getPaginationGroup().map((item, index) => (
<button
key={index}
onClick={changePage}
className={`paginationItem ${
currentPage === item ? "active" : null
}`}
>
<span>{item}</span>
</button>
))}
{/* next button */}
<button
onClick={goToNextPage}
className={`next ${currentPage === pages ? "disabled" : ""}`}
>
next
</button>
</div>
</div>
);
};
The file where I want to implement the Pagination component.
Characters.js
import { useState, useEffect } from "react";
import { CHARACTERS_PAGE_URL } from "../../api/rickNMortyApi";
import { Loading } from "../../components/Loading/Loading";
import { Character } from "./Character";
import { Pagination } from "../Pagination/Pagination";
export const Characters = () => {
const [characters, setCharacters] = useState();
useEffect(() => {
try {
fetch(CHARACTERS_PAGE_URL)
.then((res) => res.json())
.then(({ results }) => {
if (results && Array.isArray(results)) {
setCharacters(results);
}
})
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
if (!characters) {
return <Loading />;
}
return (
<div className="p-4 font-mono text-green-500 ">
<div className="flex flex-row">
<div className="m-4 ">
<label>Species</label>
<select name="species" id="species">
<option value="all">all</option>
<option value="human">human</option>
<option value="alien">alien</option>
</select>
</div>
<div className="m-4">
<label>Status</label>
<select name="status" id="status">
<option value="all">all</option>
<option value="alive">alive</option>
<option value="dead">dead</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Gender</label>
<select name="gender" id="gender">
<option value="all">all</option>
<option value="female">female</option>
<option value="male">male</option>
<option value="genderless">genderless</option>
<option value="unknown">unknown</option>
</select>
</div>
</div>
<h1 className="text-4xl">Characters</h1>
<Pagination data={characters} />
<div className="grid grid-flow-col grid-cols-5 grid-rows-4 gap-4">
{characters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))}
</div>
</div>
);
};

I've added
and now I can see prev, 1, 2, 3, 4,5, next
but charcters do not change if I click on the page number
When I try to add dataLimit={20} as a props for Pagination I'm getting an arrow below:
Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Related

options tag in react cannot print the value from sever but showing value in console window

const AddPost = () => {
const [categories, setCategory] = useState([]);
var catt = categories;
useEffect(() => {
loadAllCategories()
.then((data) => {
// console.log(data);
setCategory(data);
})
.catch((error) => {
console.log(error);
});
}, []);
const [content, setContent] = useState("");
return (
<div className="wrapper">
<Card className="shadow mt-2">
<CardBody>
<Form>
<div className="my-3">
<Label for="category">Post Category</Label>
<Input
id="category"
type="select"
className="rounded-3"
placeholder="Enter Here"
>
{/* I have tried this "Value is display on the Console window but not display inside the option tag */}
{categories.map((cat) => {
<option key={cat.categoryId} value={cat.categoryId}>
{console.log(cat.categoryTitle)}
{console.log(cat.categoryId)}
{cat.categoryTitle}
{cat.categoryId}
</option>;
// debugger;
})}
{/* Also tried to use forEach loop but same result */}
{/* {catt.forEach((cat) => {
var option = cat.categoryTitle;
debugger;
<option>
{option}
{console.log(option)}
</option>;
})} */}
</Input>
</div>
<Container className="text-center">
<Button color="primary">Create Post</Button>
<Button className="ms-2" color="danger">
Reset Content
</Button>
</Container>
</Form>
</CardBody>
{content}
</Card>
</div>
);
};
The value are getting from the server and displaying into the console window but not showing inside the option tag in react
You're not returning anything from you map function.
{
categories.map((cat) => {
return (
<option key={cat.categoryId} value={cat.categoryId}>
{cat.categoryTitle}
{cat.categoryId}
</option>
);
});
}

React Province Disctrict selector

I want to list the districts of the province selected from the province option. I am pulling from local json file.
How can I solve this problem?
<div className="flex items-center px-2">
<label for="cars">İl Seçiniz:</label>
<select onChange={(e) => handleProvince(e)}>
<option key={0} value={0}>
Lütfen bir İl seçiniz
</option>
{Provinces.map((province) => (
<option key={province.plaka} value={province.il}>
{province.il}
</option>
))}
</select>
</div>
<div className="flex items-center px-2">
<label for="cars">İlçe Seçiniz:</label>
<select>
{Provinces.map((item) => (
<option key={item.plaka} value={item.ilceleri}>
{item.il === "İstanbul" ? item.ilceleri : null}
</option>
))}
</select>
</div>
dropdown screenshot
json file
You can achieve this by correctly mapping the districts to the option field as seen in the following CodeSandbox: https://codesandbox.io/s/distracted-rain-s4tnxb?file=/src/App.js
const DATA = [
{
province: "Province A",
districts: ["PA district1", "PA district2"]
},
{
province: "Province B",
districts: ["PB district1", "PB district2"]
}
];
export default function App() {
const [province, setProvince] = useState();
const [district, setDistrict] = useState();
const selectProvince = (e) => {
const selectedProvince = DATA.find(
(entry) => entry.province === e.target.value
);
setDistrict(undefined);
setProvince(selectedProvince);
};
const selectDistrict = (e) => {
setDistrict(e.target.value);
};
return (
<div className="App">
<select onChange={selectProvince}>
{DATA.map((entry, index) => {
return (
<option key={index} value={entry.province}>
{entry.province}
</option>
);
})}
</select>
<br></br>
{!!province && (
<select onChange={selectDistrict}>
{province.districts.map((district, index) => {
return (
<option value={district} key={index}>
{district}
</option>
);
})}
</select>
)}
{!!province && (
<p>
Selected province {province.province}
<br></br>
</p>
)}
{!!district && (
<p>
Selected district {district}
<br></br>
</p>
)}
</div>
);
}

Characters filter by property value in React.js

I'm a trying to build an app based on Rick and Morty api.
I want to filter characters by species.
I've created separate components for each species value
For example:
Human.js
export const Human = ({ match }) => {
const { species } = match.params;
const [characterFilters, setCharacterFilters] = useState([]);
useEffect(() => {
try {
fetch('https://rickandmortyapi.com/api/character?species=human')
.then((res) => res.json())
.then((res) => setCharacterFilters(res))
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const { name, image } = characterFilters;
return (
<div>
<h2>Name: {name}</h2>
<div>
<img alt={name} src={image}></img>
</div>
</div>
);
};
In the characters file I've set the conditions based on select propery chosen
Characters.js
export const Characters = () => {
const [characters, setCharacters] = useState();
useEffect(() => {
try {
fetch(CHARACTERS_PAGE_URL)
.then((res) => res.json())
.then(({ results }) => {
if (results && Array.isArray(results)) {
setCharacters(results);
}
})
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const handleSpeciesChange = (e) => {
console.log("[e.target.value]", e.target.value);
switch (e.target.value) {
case "human":
return <Human />;
break;
case "alien":
return <Alien />;
break;
case "unknown":
return <Unknown />;
break;
default:
return;
}
};
const handleStatusChange = (e) => {
switch (e.target.value) {
case "alive":
return <Human />;
break;
case "dead":
return <Alien />;
break;
case "unknown":
return <Unknown />;
break;
default:
return;
}
};
if (!characters) {
return <Loading />;
}
return (
<div className="p-4 font-mono text-green-500 ">
<div className="flex flex-row">
<div className="m-4 ">
<label>Species</label>
<select name="species" id="species" onChange={handleSpeciesChange}>
<option value="all">all</option>
<option value="human">human</option>
<option value="alien">alien</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Status</label>
<select name="status" id="status" onChange={handleStatusChange}>
<option value="all">all</option>
<option value="alive">alive</option>
<option value="dead">dead</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Gender</label>
<select name="gender" id="gender">
<option value="all">all</option>
<option value="female">female</option>
<option value="male">male</option>
<option value="genderless">genderless</option>
<option value="unknown">unknown</option>
</select>
</div>
</div>
<h1 className="text-4xl">Characters</h1>
<Pagination data={characters} pageLimit={5} />
<div className="grid grid-flow-col grid-cols-5 grid-rows-4 gap-4">
{characters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))}
</div>
</div>
);
};
But I'm not getting filtered characters.
I'm new to React.
Please help me to understand, what am I doing wrong here?
Is there a possibility to created a common filter component that I would be able to apply for different properties filters?
You cannot return jsx from a callback
Make filters get stored and only render these values from the array:
export const Characters = () => {
const [characters, setCharacters] = useState();
const [ speciesFilter, setSpeciesFilter ] = useState('');
const [ status, setStatus] = useState('');
useEffect(() => {
try {
fetch(CHARACTERS_PAGE_URL)
.then((res) => res.json())
.then(({ results }) => {
if (results && Array.isArray(results)) {
setCharacters(results);
}
})
.catch((err) => console.log(err));
} catch (e) {
console.log(e);
}
}, []);
const handleSpeciesChange = (e) => {
console.log("[e.target.value]", e.target.value);
setSpeciesFilter(e.target.value)
};
const handleStatusChange = (e) => {
setStatus(e.target.value);
};
if (!characters) {
return <Loading />;
}
let filteredCharacters = characters;
if (speciesFilter) {
filteredCharacters = filteredCharacters.filter(c => c.species === speciesFilter);
}
if (status ) {
filteredCharacters = filteredCharacters.filter(c => c.status === status);
}
return (
<div className="p-4 font-mono text-green-500 ">
<div className="flex flex-row">
<div className="m-4 ">
<label>Species</label>
<select name="species" id="species" onChange={handleSpeciesChange}>
<option value="all">all</option>
<option value="human">human</option>
<option value="alien">alien</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Status</label>
<select name="status" id="status" onChange={handleStatusChange}>
<option value="all">all</option>
<option value="alive">alive</option>
<option value="dead">dead</option>
<option value="unknown">unknown</option>
</select>
</div>
<div className="m-4">
<label>Gender</label>
<select name="gender" id="gender">
<option value="all">all</option>
<option value="female">female</option>
<option value="male">male</option>
<option value="genderless">genderless</option>
<option value="unknown">unknown</option>
</select>
</div>
</div>
<h1 className="text-4xl">Characters</h1>
<Pagination data={characters} pageLimit={5} />
<div className="grid grid-flow-col grid-cols-5 grid-rows-4 gap-4">
{filteredCharacters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))}
</div>
</div>
);
};
The above example uses a filter which can be improved
what am I doing wrong here?
You are returning JSX from the callback, for example,
onChange={handleStatusChange}
Here you pass the function which will be passed the event for you to handle, the problem is, returning a component from this function doesn't do anything, it doesn't tell React to render the component you returned.
Instead, you can store the value from there in state, and do something accordingly, for example, filter the array using that value
Is there a possibility to created a common filter component that I
would be able to apply for different properties filters?
That can be done, by passing the characters to that component and the filter values
export const FilterCharacters = ({ characters, status, specie }) => {
let filteredCharacters = characters;
if (specie ) {
filteredCharacters = filteredCharacters.filter(c => c.species === specie);
}
if (status ) {
filteredCharacters = filteredCharacters.filter(c => c.status === status);
}
return filteredCharacters.map((character) => (
<div key={character.id}>
<Character character={character} />
</div>
))
}
As for your Human and specific species components, it calls the API for filtered values, but I assume you already got all values initially, so you could only filter from there, if that's not the case, you could make Human more generic by passing the species and status, and querying the API with that values

Filter fetch data react

Why filter by search work but by drop-down list not?
I don't know why this is so, maybe someone sees some error.
I would like to filter tags using checkboxes. It was hard for me to find a tutorial on the internet, I was able to find filtering by searching. It turned out that everything was set up successfully, then I wanted to set filtering by list, but here I have a problem. I don't know why the list search doesn't work - even though it seems to work on the console.
demo here:
https://codesandbox.io/s/adoring-wu-rt4wd?file=/src/styles.css
const [filterParam, setFilterParam] = useState(['All'])
const [q, setQ] = useState('')
const [searchParam] = useState(['tags'])
function search(data) {
return data.filter((item) => {
if (item.tags == filterParam) {
return searchParam.some((newItem) => {
return (
item[newItem].toString().toLowerCase().indexOf(q.toLowerCase()) > -1
)
})
} else if (filterParam == 'All') {
return searchParam.some((newItem) => {
return (
item[newItem].toString().toLowerCase().indexOf(q.toLowerCase()) > -1
)
})
}
})
}
return (
<>
<div>
<label htmlFor='search-form'>
<input
type='search'
name='search-form'
id='search-form'
className='search-input'
placeholder='Search for...'
value={q}
onChange={(e) => setQ(e.target.value)}
/>
<span className='sr-only'>Search countries here</span>
</label>
</div>
<div className='select'>
<select
onChange={(e) => {
setFilterParam(e.target.value)
// console.log(setFilterParam(e.target.value.toString()))
console.log(e.target.value.toString())
}}
aria-label='Filter Countries By Region'
>
<option value='All'>All</option>
<option value='accessibility'>accessibility</option>
<option value='javascript'>javascript</option>
<option value='css'>css</option>
<option value='advanced'>advanced</option>
<option value='svg'>svg</option>
</select>
</div>
<section className={styles.main}>
{search(data).map((item) => (
<div className={styles.card}>
<div>
<div className={styles.card__first}>
<div className={styles.card__name}>
<FaTwitter className={styles.card__icon} />
<span className={styles.card__author}>{item.authorId}</span>
</div>
<div className={styles.card__price}>
<p>{item.price}</p>
</div>
</div>
<div className={styles.card__title}>
<h1>{item.title}</h1>
</div>
<div>
<p className={styles.card__desc}>{item.description}</p>
</div>
</div>
<div className={styles.card__tags}>
{item.tags.map((t) => {
return (
<div className={styles.card__tag}>
<p>{t}</p>
</div>
)
})}
</div>
</div>
))}
</section>
</>
)
}
export default Page
You have to change a lot of things, you can try this example
import "./styles.css";
import { useState, useEffect } from "react";
import styles from "./App.module.css";
export default function App() {
const [data, setData] = useState([]);
const [filterParam, setFilterParam] = useState("All");
const [q, setQ] = useState("");
const [searchParam, setSearchParam] = useState([]);
const getData = () => {
fetch("data.json", {
headers: {
"Content-Type": "application/json",
Accept: "application/json"
}
})
.then(function (response) {
console.log(response);
return response.json();
})
.then(function (myJson) {
console.log(myJson);
setData(myJson);
});
};
useEffect(() => {
getData();
}, []);
function search(data) {
return data.filter(
(item) =>
(filterParam === "All" || item.tags.includes(filterParam)) &&
(searchParam.length === 0 ||
(searchParam.every((tag) => item.tags.includes(tag)) &&
JSON.stringify(item).toLowerCase().indexOf(q.toLowerCase()) > -1))
);
}
const inputChangedHandler = (event) => {
const value = event.target.value;
const index = searchParam.indexOf(value);
if (index > -1) {
const updatedParam = [...searchParam];
updatedParam.splice(index, 1);
setSearchParam(updatedParam);
} else {
setSearchParam([...searchParam, event.target.value]);
}
};
return (
<>
<div>
<label htmlFor="search-form">
<input
type="search"
name="search-form"
id="search-form"
className="search-input"
placeholder="Search for..."
value={q}
onChange={(e) => setQ(e.target.value)}
/>
<span className="sr-only">Search countries here</span>
</label>
</div>
<div className="select">
<select
onChange={(e) => {
setFilterParam(e.target.value);
// console.log(setFilterParam(e.target.value.toString()))
console.log(e.target.value.toString());
}}
aria-label="Filter Countries By Region"
>
<option value="All">All</option>
<option value="accessibility">accessibility</option>
<option value="javascript">javascript</option>
<option value="css">css</option>
<option value="advanced">advanced</option>
<option value="svg">svg</option>
</select>
</div>
<div>
<input
type="checkbox"
id="topping"
name="advanced"
value="advanced"
onChange={inputChangedHandler}
/>
advanced
</div>
<div>
<input
type="checkbox"
id="topping"
name="javascript"
value="javascript"
onChange={inputChangedHandler}
/>
javascript
</div>
<div>
<input
type="checkbox"
id="topping"
name="fundamentals"
value="fundamentals"
onChange={inputChangedHandler}
/>
fundamentals
</div>
<div>
<input
type="checkbox"
id="topping"
name="css"
value="css"
onChange={inputChangedHandler}
/>
css
</div>
<div>
<input
type="checkbox"
id="topping"
name="svg"
value="svg"
onChange={inputChangedHandler}
/>
svg
</div>
<div>
<input
type="checkbox"
id="topping"
name="accessibility"
value="accessibility"
onChange={inputChangedHandler}
/>
accessibility
</div>
<section className={styles.main}>
{search(data).map((item) => (
<div className={styles.card}>
<div>
<div className={styles.card__first}>
<div className={styles.card__name}>
<p className={styles.card__icon}>ICON</p>
<span className={styles.card__author}>{item.authorId}</span>
</div>
<div className={styles.card__price}>
<p>{item.price}</p>
</div>
</div>
<div className={styles.card__title}>
<h1>{item.title}</h1>
</div>
<div>
<p className={styles.card__desc}>{item.description}</p>
</div>
</div>
<div className={styles.card__tags}>
{item.tags.map((t) => {
return (
<div className={styles.card__tag}>
<p>{t}</p>
</div>
);
})}
</div>
</div>
))}
</section>
{/* <Margins>
<Header data={data} />
<Main data={data} />
</Margins> */}
</>
);
}
enter link description here

Modify prop in list of components generated in a loop

I have a checkbox component that has a disabled property. My goal is:
When 3 checkboxes are selected set the disabled property to true in all checkboxes that are not selected.
Each time a checkbox is selected show an updated count of remaining selection allowed.
For goal 1 I am not sure what the right way to do this is. Do I need to create an array of refs?
For goal 2 I have added this code:
{3 - Object.keys(selected).length + " domains remaining"}
But when selected is updated the render does not get the updated value.
My code with comment where I want to disable/enable all is on codesandbox here: https://codesandbox.io/s/exciting-bush-119ef?file=/src/App.js
Also pasted here below:
import React, { useState } from "react";
export default function App(props) {
const [enableSelection, setEnableSelection] = useState(true);
const [selected, setSelected] = useState({});
let domains = [
"example1.com",
"example2.com",
"example3.com",
"example4.com",
"example5.com",
"example6.com",
"example7.com",
"example8.com",
"example9.com",
"example10.com",
"example11.com",
"example12.com",
"example13.com",
"example14.com",
"example15.com",
"example16.com"
];
function handleChange(index, event) {
console.log(index);
console.log(event.target.name);
console.log(event.target.value);
console.log(event.target.checked);
if (event.target.checked === true) {
setSelected((prev) => {
prev[index] = event.target.value;
return prev;
});
} else {
setSelected((prev) => {
delete prev[index];
return prev;
});
}
if (Object.keys(selected).length >= 3) {
//disable all other input
} else {
//make sure all other enabled
}
}
return (
<div className="">
<div className="">
<SubHead
text={3 - Object.keys(selected).length + " domains remaining"}
/>
{domains.map((domain, i) => (
<div key={i} className="odd:bg-gray-200">
<CheckBox
disabled={false}
label={domain}
name={"checkbox_" + i}
value={domain}
onChange={(index, e) => handleChange(i, e)}
/>
</div>
))}
</div>
</div>
);
}
function CheckBox({
disabled,
key,
label,
selected,
name,
value,
onChange: parentChange
}) {
const [selectedState, setSelectedState] = useState(selected ?? false);
function handleChange(event) {
setSelectedState(!selectedState);
parentChange(key, event);
}
return (
<label key={key} className="inline-flex items-center mt-3">
<input
id={name}
name={name}
value={value}
onChange={handleChange}
type="checkbox"
className="form-checkbox h-5 w-5 text-gray-600"
disabled={disabled}
checked={selected}
/>
<span
className={
"ml-2 " +
(selectedState ? "font-semibold " : "") +
(disabled ? "text-gray-400" : "text-gray-700")
}
>
{label}
</span>
</label>
);
}
function SubHead(props) {
return <h3 className="font-medium text-xl text-center">{props.text}</h3>;
}
The problem was that you handle the event in two places,
You don't need to handle the state of the checkbox twice, once in the parent and another inside it. I also converted the selected to array to make it easier:
Here is the demo: https://codesandbox.io/s/boring-sun-35lfk?file=/src/App.js
import React, { useState } from "react";
export default function App(props) {
const [selected, setSelected] = useState([]);
let domains = [
"example1.com",
"example2.com",
"example3.com",
"example4.com",
"example5.com",
"example6.com",
"example7.com",
"example8.com",
"example9.com",
"example10.com",
"example11.com",
"example12.com",
"example13.com",
"example14.com",
"example15.com",
"example16.com"
];
function handleChange(event) {
console.log(event.target.name);
console.log(event.target.value);
console.log(event.target.checked);
if (event.target.checked) {
setSelected((prev) => [...prev, event.target.name]);
} else {
setSelected((prev) => prev.filter((name) => name !== event.target.name));
}
if (Object.keys(selected).length >= 3) {
//disable all other input
} else {
//make sure all other enabled
}
}
const remaining = 3 - Object.keys(selected).length;
return (
<div className="">
<div className="">
<SubHead text={remaining + " domains remaining"} />
{domains.map((domain) => {
const checked = selected.indexOf("checkbox_" + domain) > -1;
return (
<div key={domain} className="odd:bg-gray-200">
<CheckBox
checked={checked}
disabled={!checked && remaining === 0}
label={domain}
name={"checkbox_" + domain}
value={domain}
onChange={handleChange}
/>
</div>
);
})}
</div>
</div>
);
}
function CheckBox({
checked,
disabled,
key,
label,
selected,
name,
value,
onChange: parentChange
}) {
return (
<label key={key} className="inline-flex items-center mt-3">
<input
id={name}
name={name}
value={value}
onChange={parentChange}
type="checkbox"
className="form-checkbox h-5 w-5 text-gray-600"
disabled={disabled}
checked={selected}
/>
<span
className={
"ml-2 " +
(checked ? "font-semibold " : "") +
(disabled ? "text-gray-400" : "text-gray-700")
}
>
{label}
</span>
</label>
);
}
function SubHead(props) {
return <h3 className="font-medium text-xl text-center">{props.text}</h3>;
}

Resources