Delete Table Row not working properly with search bar - javascript

i'm new to reactjs and i'm trying to make a table that shows the information from a array of objects and have a button of delete and an input to search among the users. The delete button is working correctly when i'm not searching anything, but when i'm searching it doesn't delete the corretly row, and deletes only the first one. I see that it is because the arrays that show the table are different with and without the search being used but I don't know how to make it work.
this is the component of the table:
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(index)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
Here the component of the search bar:
import "./searchBar.css"
function SearchBar({ searchedData, onSearch }) {
return (
<div className="search-bar">
<label>Search</label>
<input type="text" placeholder="Search User" value={searchedData} onChange={e => onSearch(e.target.value)} />
</div>
);
}
export default SearchBar;
and here is the home:
import "./Home.css";
import React, { useEffect, useState } from "react";
import Header from "../components/Header/Header";
import Table from "../components/Table/Table";
import AddData from "../components/AddData/AddData";
import SearchBar from "../components/SearchBar/SearchBar";
import { userArr } from "../mock/users";
const Home = () => {
const headerUser = ["Name", "Email", "Occupation", "Birthday"];
const [newUserArr, setNewUserArr] = useState(userArr);
const [searchedItem, setSearchedItem] = useState("");
const searchedArray = newUserArr.filter((item) => {
if (item.name.toLowerCase().includes(searchedItem.toLowerCase())) {
return true;
}
});
function onSearch(e) {
setSearchedItem(e);
}
const addDataToArr = (form) => {
setNewUserArr([...newUserArr, form]);
};
const deleteData = (indexUserArr) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => ind !== indexUserArr
);
setNewUserArr(restOfDataArray);
};
return (
<>
<Header />
<SearchBar searchedData={searchedItem} onSearch={onSearch} />
<Table
type="user"
headerData={headerUser}
bodyData={newUserArr}
removeItem={(index) => deleteData(index)}
/>
<AddData saveData={(val) => addDataToArr(val)} />
</>
);
};
export default Home;
thank you

If you have ID in your user data then use that instead of index or create id keywords using concatenate with your values here is examples.
import { formatDate } from "../../utils/formatDate";
import "./table.css";
import { useState } from "react";
function Table(props) {
const { headerData, bodyData, type, removeItem} = props;
const isUser = type === "user";
const buildTableItems = () => {
return bodyData.map((item, index) => (
<tr className="data-tr">
<td>{item.name}</td>
<td>{item.email}</td>
<td>{item.occupation}</td>
<td>{formatDate(item.birthday)}</td>
<td>
<button className="delete-button" onClick={() => removeItem(`${item.name}${item.email}${item.occupation}`)}>
Delete
</button>
</td>
</tr>
));
};
return (
<div className="user-data">
<table className="user-table">
<thead>
<tr className="data-th">
{headerData.map((headerTable) => (
<th >{headerTable}</th>
))}
</tr>
</thead>
<tbody>{buildTableItems()}</tbody>
</table>
</div>
);
}
export default Table;
And here is your delete method ${item.name}${item.email}${index}
const deleteData = (data) => {
let restOfDataArray = newUserArr.filter(
(element, ind) => `${element.name}${element.email}${element.occupation}` !== data
);
setNewUserArr(restOfDataArray);
};
This will fixed your problem. If this doesn't work then you need to use ID to resolve this problem. There is a possibility that ${item.name}${item.email}${item.occupation} can be duplicates.
Never use index ever for deleting or any other operations. Use always ID.

Related

How to display different tables on click in React?

I am making a simple todo. I am fetching data from an API and I want to show all the items in a table by default. There will be 3 buttons - All, Complete and Incomplete which will show All, Completed and Incompleted todos table respectively. I have set states for completed and incompleted todos but can't wrap my head around how to perform conditional rendering and display different tables on different button clicks.
Below is my code -
import React, { useState, useEffect } from "react";
import axios from "axios";
import "./style.css";
export default function App() {
const URL = 'https://jsonplaceholder.typicode.com/todos';
const [todo, setTodo] = useState([]);
const [completed, setCompleted] = useState([]);
const [incomplete, setIncomplete] = useState([]);
useEffect(()=>{
axios.get(URL)
.then(res=>setTodo(res.data));
},[])
const showCompleted = () =>{
const completeTask = todo.filter((items)=>items.completed===true);
setCompleted(completeTask);
}
const showIncomplete = () =>{
const incompleteTask = todo.filter((items)=>items.completed===false);
setIncomplete(incompleteTask);
}
return (
<div>
<h1>ToDos!</h1>
<button type="button">All</button>
<button type="button" onClick={showCompleted}>Completed</button>
<button type="button" onClick={showIncomplete}>Incomplete</button>
<hr />
<table>
<tr>
<th>ID</th>
<th>Title</th>
<th>Completed</th>
</tr>
{todo.map((items)=>
<tr key={items.id}>
<td>{items.id}</td>
<td>{items.title}</td>
<td><input type="checkbox" defaultChecked={items.completed ? true : false} /></td>
</tr>
)}
</table>
</div>
);
}
Instead of maintaining a separate state for each type have one type state that the buttons update when they're clicked. Add data attributes to the buttons to indicate what type they are and which can be picked up in the click handler.
Instead of mapping over the whole set of todos, call a function that filters out the set of data from the todo state that you need.
const { useEffect, useState } = React;
const URL = 'https://jsonplaceholder.typicode.com/todos';
function Example() {
const [todos, setTodos] = useState([]);
const [type, setType] = useState('all');
useEffect(()=>{
fetch(URL)
.then(res => res.json())
.then(data => setTodos(data));
}, []);
// Filter the todos depending on type
function filterTodos(type) {
switch(type) {
case 'completed': {
return todos.filter(todo => todo.completed);
}
case 'incomplete': {
return todos.filter(todo => !todo.completed);
}
default: return todos;
}
}
// Set the type when the buttons are clicked
function handleClick(e) {
const { type } = e.target.dataset;
setType(type);
}
// Call the filter function to get the
// subset of todos that you need based
// on the type
return (
<div>
<h1>ToDos!</h1>
<button
type="button"
className={type === 'all' && 'active'}
data-type="all"
onClick={handleClick}
>All
</button>
<button
type="button"
className={type === 'completed' && 'active'}
data-type="completed"
onClick={handleClick}
>Completed
</button>
<button
type="button"
className={type === 'incomplete' && 'active'}
data-type="incomplete"
onClick={handleClick}
>Incomplete
</button>
<hr />
<table>
<tr>
<th>ID</th>
<th>Title</th>
<th>Completed</th>
</tr>
{filterTodos(type).map(todo => {
const { id, title, completed } = todo;
return (
<tr key={id}>
<td>{id}</td>
<td>{title}</td>
<td>
<input
type="checkbox"
defaultChecked={completed ? true : false}
/>
</td>
</tr>
);
})}
</table>
</div>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
button { margin-right: 0.25em; }
button:hover { cursor:pointer; }
.active { background-color: lightgreen; }
<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>
Keep two states, one to store the initial data and another one to keep track of actually displayed data.
Try like this:
function App() {
const URL = "https://jsonplaceholder.typicode.com/todos";
const [todo, setTodo] = React.useState([]);
const [view, setView] = React.useState([]);
React.useEffect(() => {
fetch(URL)
.then((res) => res.json())
.then((result) => {
setTodo(result);
setView(result);
});
}, []);
const showAll = () => {
setView(todo);
};
const showCompleted = () => {
const completeTask = todo.filter((items) => items.completed === true);
setView(completeTask);
};
const showIncomplete = () => {
const incompleteTask = todo.filter((items) => items.completed === false);
setView(incompleteTask);
};
return (
<div>
<h1>ToDos!</h1>
<button type="button" onClick={showAll}>
All
</button>
<button type="button" onClick={showCompleted}>
Completed
</button>
<button type="button" onClick={showIncomplete}>
Incomplete
</button>
<hr />
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
{view.map((items) => (
<tr key={items.id}>
<td>{items.id}</td>
<td>{items.title}</td>
<td>
<input
type="checkbox"
defaultChecked={items.completed ? true : false}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
You can use useMemo to prepare the data to display based on some conditions/filters/search/ordering/ anything else.
So few steps to achieve that:
Optional, declare some object outside of the component to hold some constants. Maybe I choosed a poor name for that but the idea itself should be ok. FILTER_COMPLETED in the code.
Add a useState variable to hold active filter for this specific area. const [filterCompleteMode, setFilterCompleteMode] = useState(...) in the code.
Add a useMemo variable that will prepare the data to display. You can apply some ordering or additinal filtering here. todosToDisplay in the code.
Modify your JSX a bit, change <button>s and todo to todosToDisplay.
const { useState, useMemo, useEffect } = React;
const FILTER_COMPLETED = {
All: "ALL",
Complete: "COMPLETE",
Incomplete: "INCOMPLETE"
};
function App() {
const URL = "https://jsonplaceholder.typicode.com/todos";
const [todos, setTodos] = useState([]);
const [filterCompleteMode, setFilterCompleteMode] = useState(
FILTER_COMPLETED.All
);
const todosToDisplay = useMemo(() => {
if (!todos) return [];
switch (filterCompleteMode) {
case FILTER_COMPLETED.All:
return todos;
case FILTER_COMPLETED.Incomplete:
return todos.filter((x) => x.completed === false);
case FILTER_COMPLETED.Complete:
return todos.filter((x) => x.completed === true);
default:
return todos;
}
}, [todos, filterCompleteMode]);
useEffect(() => {
fetch(URL)
.then((res) => res.json())
.then((data) => setTodos(data));
}, []);
const onCompleteFilterClick = (e) => {
setFilterCompleteMode(e.target.dataset.mode);
};
return (
<div>
<h1>ToDos!</h1>
<button
type="button"
data-mode={FILTER_COMPLETED.All}
onClick={onCompleteFilterClick}
>
All
</button>
<button
type="button"
data-mode={FILTER_COMPLETED.Complete}
onClick={onCompleteFilterClick}
>
Completed
</button>
<button
type="button"
data-mode={FILTER_COMPLETED.Incomplete}
onClick={onCompleteFilterClick}
>
Incomplete
</button>
<hr />
<table>
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Completed</th>
</tr>
</thead>
<tbody>
{todosToDisplay.map((item) => (
<tr key={item.id}>
<td>{item.id}</td>
<td>{item.title}</td>
<td>
<input
type="checkbox"
defaultChecked={item.completed ? true : false}
/>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Create state:
const [whatShow, setWhatShow] = useState('All').
When you click on button change this state
next:
{todo.map((items)=>
{items.completed === whatShow && <tr key={items.id}>
<td>{items.id}</td>
<td>{items.title}</td>
<td><input type="checkbox" defaultChecked={items.completed ? true : false} /></td>
</tr>}
)}
something like this

How can I toggle the display of an array that is located within each object of another array that comes from an API?

For example:
I'm displaying multiple Student objects to the website that's coming from an API. Each Student object contains an array of their test Grades along with other student information. Now, I want to display each Student's information onto the screen but instead of displaying the student's Grades, I want each Student to have a "+" Button which will be to toggle the display of the student's Grades.
I'm using the useState hook from React. My problem is that when I click the "+" Button, it toggles ALL student's grades on/off. What I want is to toggle the Grades of only the student whose button I clicked.
Here's my code:
import axios from 'axios';
import { useState, useEffect } from 'react';
import styles from "./Home.module.css";
import { v4 as uuidv4 } from 'uuid';
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const [isShown, setIsShown] = useState(true);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
const toggleGrades = () => {
console.log("toggle");
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName} {student.lastName}</p>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
<button onClick={() => {
setIsShown(!isShown);
}}>
+
</button>
{isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
</ul>
</div>
</div>
)
)}
</div>
</>
)
}
export default Home;
Just remove the mapping part to another component and import it in map part. In this way you will have still same structure and it will work without any extra logic
NEW COMPONENT
const Students = ({student}) => {
const [isShown, setIsShown] = useState(true);
return (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName} {student.lastName}</p>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
<button onClick={() => {
setIsShown(!isShown);
}}>
+
</button>
{isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
</ul>
</div>
</div>
)
}
HOME COMPONENT
import axios from 'axios';
import { useState, useEffect } from 'react';
import styles from "./Home.module.css";
import { v4 as uuidv4 } from 'uuid';
const Home = () => {
const [students, setStudents] = useState([]);
const [filteredStudents, setFilteredStudents] = useState([]);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setFilteredStudents(response.data.students);
console.log(response.data.students);
}
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const searchStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setFilteredStudents(newArray);
}
const toggleGrades = () => {
console.log("toggle");
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input type="text" placeholder="Search by name" onChange={(event) => searchStudentName(event.target.value) }/>
{filteredStudents.map((student) => (
<Student student={student} />
)
)}
</div>
</>
)
}
export default Home;
Note: I did not include export import parts of component and css but it's the easy part

How can I prevent the array of identifier tags for each student disappearing after re-fetching from API?

I have a list of students that will display onto the web browser depending on what you filter by name/tag. If those filter fields become empty, the page re-fetches all the students from an API and displays them.
The tags are stored in an array using useState for each Student object.
Example Problem: After adding a tag to a student, then somehow filtering the students, and then finally clearing the filter fields, all the students will be displayed again but WITHOUT their tags.
Expected Outcome: I need the student to keep their tags, at least for a current session on the website.
Question: How can I solve this? Should I use localStorage? or a Database such as MongoDB? or something else?
Students.jsx
import { useState } from 'react';
import styles from "../views/Home.module.css";
import { v4 as uuidv4 } from 'uuid';
import AddIcon from '#mui/icons-material/Add';
import RemoveIcon from '#mui/icons-material/Remove';
const Students = ({student}) => {
const [isShown, setIsShown] = useState(true);
const [tags, setTags] = useState([]);
const findAverageGrade = arr => {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += parseInt(arr[i]);
}
return sum / arr.length;
}
const addTag = (event) => {
if (event.key === 'Enter') {
setTags([...tags, event.target.value])
event.target.value = "";
}
}
return (
<div key={student.email} className={styles.studentItem}>
<img className={styles.studentImage} src={student.pic} />
<div className={styles.studentInfoContainer}>
<div className={styles.studentHeader}>
<p className={styles.studentName}>{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}</p>
<button className={styles.expandBtn} onClick={() => {
setIsShown(!isShown);
}}>
{ isShown ? <AddIcon className={styles.expandBtn} /> : <RemoveIcon className={styles.expandBtn} /> }
</button>
</div>
<ul className={styles.studentDetail}>
<li>Email: {student.email}</li>
<li>Company: {student.company}</li>
<li>Skill: {student.skill}</li>
<li>Average: {findAverageGrade(student.grades)}%</li>
{!isShown ? <div>
<table className={styles.gradesTable}>
<tbody>
{student.grades.map((grade) => (
<tr key={uuidv4()}>
<td>Test</td>
<td>{grade}%</td>
</tr>
))}
</tbody>
</table>
</div>
: null }
<div className={styles.tagOutput}>
{tags.map(tag => (<p className={styles.tag}>{tag}</p>))}
</div>
<input id="tag-input" className={styles.addTagInput} type="text" placeholder="Add a tag" onKeyPress={(e) => addTag(e)}/>
</ul>
</div>
</div>
)
}
export default Students;
Home.jsx
import axios from 'axios';
import { useState, useEffect } from 'react';
import Students from '../components/Students';
import styles from "./Home.module.css";
const Home = () => {
const [students, setStudents] = useState([]);
const [nameFilteredStudents, setNameFilteredStudents] = useState([]);
const [tagFilteredStudents, setTagFilteredStudents] = useState([]);
const fetchStudents = async () => {
const response = await axios.get(`https://api.hatchways.io/assessment/students`);
setStudents(response.data.students);
setNameFilteredStudents(response.data.students);
console.log(response.data.students);
}
const filterStudentName = async (searchName) => {
const searchNameFiltered = searchName.toLowerCase();
console.log(searchNameFiltered);
if (searchNameFiltered === "") {
fetchStudents();
return;
}
var newArray = await students.filter((student) => {
return student.firstName.toLowerCase().includes(searchNameFiltered)
|| student.lastName.toLowerCase().includes(searchNameFiltered);
})
await setNameFilteredStudents(newArray);
}
const filterStudentTag = async (searchTag) => {
const searchTagFiltered = searchTag.toLowerCase();
console.log(searchTagFiltered)
console.log(students.filter((student) => {
console.log(student);
}))
// var newArray = await students.filter((student) => {
// return student.firstName.toLowerCase().includes(searchNameFiltered)
// || student.lastName.toLowerCase().includes(searchNameFiltered);
// })
}
useEffect(() => {
fetchStudents();
}, [])
return(
<>
<div>
<input className={styles.searchInput} type="text" placeholder="Search by name" onChange={(event) => filterStudentName(event.target.value) }/>
<input className={styles.searchInput} type="text" placeholder="Search by tag" onChange={(event) => filterStudentTag(event.target.value) }/>
{nameFilteredStudents.map((student) => (
<Students key={student.id} student={student} />
))}
</div>
</>
)
}
export default Home;
Since you are passing the students prop to the child component, any time the students change the component will be re-rendered. Also since the filter is in the parent component, the child component will re-render because you are calling fetchStudents() in the filter function. You can toy with changing how you filter the students.

How to implement pagination to a list data in a table in react.js?

Quick help needed! I have list of data rendered in a table from an API. I need this list of data to be paginated into small list of data.
Here is the code for VendorsDetail.js which displays list of data in a table
import React, { useState, useEffect } from "react";
import HelpOutlineIcon from "#mui/icons-material/HelpOutline";
import axios from "axios";
import VendorDetailsBrief from "./VendorDetailsBrief";
import Pagination from "./Pagination";
const VendersDetail = ({ textMe }) => {
const [data, setData] = useState({});
const foo = "cpe:2.3:a:oracle:peoplesoft_enterprise:8.22.14";
const baseURL =
"https://services.nvd.nist.gov/rest/json/cves/1.0?cpeMatchString=" + foo;
useEffect(() => {
axios
.get(baseURL)
.then((response) => {
setData(response.data);
})
.then(
(response) => {},
(err) => {
alert(err);
}
);
}, []);
const DisplayData = data?.result?.CVE_Items?.map((vender) => {
return (
<tr>
<td className="color_id font-semibold">
{vender?.cve?.CVE_data_meta?.ID}
</td>
<td className="w-96">
{vender?.cve?.description?.description_data?.[0]?.value}
</td>
<td>{vender?.impact?.baseMetricV2?.exploitabilityScore}</td>
<td>{vender?.impact?.baseMetricV2?.severity}</td>
<td>{vender?.impact?.baseMetricV2?.impactScore}</td>
</tr>
);
});
return (
<div className="z-100 flex justify-center items-center mt-10">
<div className="text-black">
<div className="rounded overflow-hidden flex justify-center items-center">
<table class="table table-striped ">
<thead>
<tr>
<th>Vuln ID</th>
<th>Description Data</th>
<th>Exploitability Score</th>
<th>Severity</th>
<th>Impact Score</th>
</tr>
</thead>
<tbody>{DisplayData}</tbody>
</table>
</div>
</div>
</div>
);
};
export default VendersDetail;
Pagination.js
import React from "react";
const Pagination = ({ postsPerPage, totalPosts, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalPosts / postsPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<ul className="pagination">
{pageNumbers.map((number) => (
<li key={number} className="page-item">
<a onClick={() => paginate(number)} href="!#" className="page-link">
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
export default Pagination;
How can I implement pagination in this particular data list? Thanks
Create Parent Component with logic to get data from URL and pagination onCLick handler.
Parent Component should render VendorsDetail component and Pagination component.
Pass data to be displayed to VendorsDetails component and getSubsequentData handler to Pagination component.
If user click on specific page number, call getSubsequentData handler with specific argument, that updates the state of the parent component, which will updates VendorsDetail component.
const ParentComponent = () => {
const [data, setData] = useState({})
useEffect = (() => {
// axios call to get initial data from the URL
})
getSubsequentData = (URL) => {
// axios call to get data based on the URL.
// LInk this to pagination onClick
}
return(
<VendorsDetail data={data} />
<Pagination getNextData={getSubsequentData}/>
)
}

React: TypeError: dispatch is not a function

I want to fetch from redux. I am following this tutorial: https://codesandbox.io/s/react-redux-application-hewdb?file=/src/pages/PostsPage.js
But when i used it in my code that is:
import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import {fetchInterview} from '../actions/interviewActions'
const DetailInterview = (props, { dispatch, loading, interviews, hasErrors }) => {
console.log("test interview",interviews)
useEffect(() => {
const { match: { params: { id } } } = props;
dispatch(fetchInterview(id))
}, [dispatch])
const interviewslist = interviews
console.log('interview: ', interviews)
return (
<div>
<h3>All participants</h3>
<table>
<thead>
<tr>
<th>ID</th>
<th>Interview id</th>
<th>Partcipants id</th>
<th>Time</th>
</tr>
</thead>
<tbody>
{
console.log('interviews:sad ', interviews)
}
{
interviews? interviews.map((interview) => {
console.log('sadassad',interview)
console.log('sadaghahhgsghssad',interviews)
return (
<tr key={interview.id}>
<td>{interview.id}</td>
<td>{interview.interview_id}</td>
<td>
{/* <Link to={`/posts/${post.id}`}> */}
{interview.participant_id}
{/* </Link> */}
</td>
<td>{interview.created_at}</td>
</tr>
)
}) : null
}
</tbody>
</table>
</div>
);
}
// export default DetailInterview;
const mapStateToProps = state => ({
loading: state.interview.loading,
interviews: state.interview.interview,
hasErrors: state.interview.hasErrors,
})
export default connect(mapStateToProps)(DetailInterview)
I get an error: Uncaught TypeError: dispatch is not a function
Could not understand what is the error behind this.
You are destructuring values from the second argument of DetailInterview whereas you should do that from props as values from mapStateToProps and connect are available as props to the connected component
const DetailInterview = (props) => {
const { dispatch, loading, interviews, hasErrors } = props;
...
}

Categories

Resources