Live pagination on search bar with hooks in reactjs - reactjs

Hello guys i am trying to implement the live pagination of searched items. I have done only the pagination for all the products but not for the searching ones and i am having some problems into writting it.
Thank you for your time.
This is app.js:
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [currentData, setCurrentData] = useState([]);
const [columns, setColumns] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [dataPerPage, setDataPerPage] = useState(10);
const [searchTerm, setSearchTerm] = useState("");
const [filteredProduct, setFilteredProducts] = useState("");
useEffect(() => {
var myHeaders = new Headers();
myHeaders.append("Accept", "text/plain");
myHeaders.append(
"Authorization",
"Bearer xxx"
);
var requestOptions = {
method: "GET",
headers: myHeaders,
redirect: "follow",
};
fetch("http://localhost:5000/api/WarehousStock", requestOptions)
.then((response) => response.json())
.then((result) => {
setData(result);
})
.catch((error) => console.log("error", error))
.finally(() => setLoading(false));
}, []);
// this will run evertime one of the following state will change => data, currentPage, dataPerPage
useEffect(() => {
// generate dynamically columns from first object from array
setColumns(
Object.keys(data[0] || []).map((key) => ({
Header: key,
accessor: key,
}))
);
filterData();
}, [data, currentPage, dataPerPage]);
var paginate = (pageNumber) => setCurrentPage(pageNumber);
function filterData() {
const indexOfLastData = currentPage * dataPerPage;
const indexOfFirstData = indexOfLastData - dataPerPage;
// if there is a search term
if (searchTerm !== '') {
let result =
data.filter(data => {
return data.articleName.toLowerCase().includes(searchTerm.toLowerCase())
})
result = result.slice(indexOfFirstData, indexOfLastData)
setCurrentData([...result])
} else {
// if there is no a search term
setCurrentData(data.slice(indexOfFirstData, indexOfLastData));
}
}
useEffect(() => {
filterData();
}, [searchTerm])
if (loading) return <p>Loading...</p>;
return (
<Styles>
<div className="SearchButton"><input type="text" placeholder="Search name of product" onChange={event => { setSearchTerm(event.target.value) }} /></div>
<Table columns={columns} data={currentData} />
<Pagination
dataPerPage={dataPerPage}
totalData={data.length}
paginate={paginate}
/>
</Styles>
);
}
This is the pagination component:
const Pagination = ({ dataPerPage, totalData, paginate }) => {
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(totalData / dataPerPage); i++) {
pageNumbers.push(i);
}
return (
<nav>
<ul className="pagination mt-4">
{pageNumbers.map((number) => (
<li key={number} className="page-item">
<a onClick={() => paginate(number)} href="!#" className="page-link">
{number}
</a>
</li>
))}
</ul>
</nav>
);
};
Thank you for your help!

Related

How to call function in a different component in React.js?

I have two components.
DropDownForRoomChangeCondo.js js that displays radio buttons. DiscoverCondoRoom.js displays DropDownForRoomChangeCondo.js.
What I want to achieve is In DropDownForRoomChangeCondo.js, When sending a request to the backend with handleChange (when switching the radio button) I want to change the screen display by calling getDevices(); in DiscoverCondoRoom.js. (Since the assignment of the room where the device is installed changes when the radio button is switched, I want to update the display)
Issue/error message
Currently, when sending a request to the backend with handleChange (when switching the radio button) Display update does not occur.
DropDownForRoomChangeCondo.js
import Dropdown from 'react-bootstrap/Dropdown';
const DropDownForRoomChangeCondo = (item) => {
const history = useHistory();
const [devices, setDevices] = useState([]);
const handleChange = e => {
setVal(e.target.name);
setDeviceRoomName(e.target.name);
}
const setDeviceRoomName = async(data) => {
console.log("Body sent to server", {
attributes:
[
{
entity_id : item.item.entity_id,
room_name: data
}
]
})
await axios.post('xxx.com',
{
attributes:
[
{
entity_id : item.item.entity_id,
room_name: data
}
]
},
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
console.log('Set Device Room Name!');
getDevices();
})
.catch(err => {
console.log(err);
console.log('Missed Set Device Room Name!');
});
}
const getDevices = async(data) => {
await axios.get('xxx.com',
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
console.log(result.data)
console.log("bbbbbbbbbbb")
setDevices(result.data.attributes);
})
.catch(err => {
console.log(err);
});
}
const keys = [
"camera",
"climate",
"cover",
"light",
"lock",
"sensor",
"switch",
];
const entities = keys
.map((key) => (devices[key] || []).map((e) => ({ ...e, key })))
.flat();
const roomNames = [...new Set(entities.map((entity) => entity.room_name))];
const [val, setVal] = useState(item.item.room_name);
console.log(val)
console.log(typeof(val))
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
<img className="ic_edit" src={ic_edit} />
</a>
));
useEffect(() => {
getDevices();
},[]);
return (
<>
<div className="">
<p>{item.item.room_name}</p>
<Dropdown className="room_change_dropdown_top">
<Dropdown.Toggle as={CustomToggle} id="dropdown-custom-components" />
<Dropdown.Menu className="room_change_dropdown">
<Dropdown.Item className="room_change_dropdown_item">
{roomNames.map((room_names, i) => (
<div className="flex_radio">
<input
className="room_change_radio"
type="radio"
value={room_names}
name={room_names}
onChange={handleChange}
checked={val === room_names}
/>
<p className="drop_down_p">{room_names}</p>
</div>
))}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</div>
</>
);
}
export default DropDownForRoomChangeCondo;
DiscoverCondoRoom.js
const DiscoverCondoRoom = () => {
const history = useHistory();
const [devices, setDevices] = useState([]);
const getDevices = async(data) => {
await axios.get('xxx.com',
{
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${cookies.get('accesstoken')}`
},
})
.then(result => {
setDevices(result.data.attributes);
})
.catch(err => {
console.log(err);
});
}
useEffect(() => {
getDevices();
},[]);
const lll = Object.keys(devices);
const object_device_value = Object.values(devices).flat();
const keys = [
"camera",
"climate",
"cover",
"light",
"lock",
"sensor",
"switch",
];
const entities = keys
.map((key) => (devices[key] || []).map((e) => ({ ...e, key })))
.flat();
const roomNames = [...new Set(entities.map((entity) => entity.room_name))];
return (
<>
<div className="container condo_container">
{entities.map((entity, i) => (
<DropDownForRoomChangeCondo item={entity} />
))}
</div>
</>
);
}
};
export default DiscoverCondoRoom;
You need to pass your getDevices Method as a prop to your dropdown component.
<DropDownForRoomChangeCondo item={entity} getDevices={getDevices} />
Then inside your DropDown Component, call the getDevices Method at your desired place by calling props.getDevices().
Also, i would suggest to define props like so:
const DropDownForRoomChangeCondo = (props) => {
const history = useHistory();
const [devices, setDevices] = useState([]);
…
And then access item by pointing to props.item

I am trying to fetch movie by genres from movie database

I am struggling to fetch movies by its genres from API movies database. The error is always say that 'genres.map is not a function' or when I try to call selectedGenre to fetch the movie that associate with each genres by using document.GetElementById.value, it cannot read the .value thing. What I am doing wrong? Thank you ahead
const Discover = () => {
const navigate = useNavigate();
const [movies, setMovies] = useState([]);
const [genres, setGenres] = useState('');
const [searchTerm, setSearchTerm] = useState('');
const currentGenre = document.getElementById('genres').value;
const FEATURED_API = `https://api.themoviedb.org/3/discover/movie?sort_by=popularity.desc&api_key=9a7243213d79e4344f8f16ce3b6098cf`;
const GENRES_API = 'https://api.themoviedb.org/3/genre/movie/list?api_key=9a7243213d79e4344f8f16ce3b6098cf';
const SEARCH_API = 'https://api.themoviedb.org/3/search/movie?api_key=9a7243213d79e4344f8f16ce3b6098cf&query='
useEffect(() => {
getMovies(FEATURED_API),
getGenres(GENRES_API)
}, []);
const getGenres = (API) => {
fetch(API)
.then(res => res.json())
.then((data) => {
setGenres(data.genres);
console.log(data.genres)
})
}
const getMovies = (API) => {
fetch(API)
.then((res) => res.json())
.then((data) => {
setMovies(data.results);
console.log(data.results);
});
};
const handleOnSubmit = (e) => {
e.preventDefault();
if(currentGenre) {
fetch(FEATURED_API + `&with_genres=${currentGenre}`)
.then((res) => res.json())
.then((data) => {
setMovies(data.results)
console.log(data.results)
})
}
setGenres('')
// navigate(`/genre/movie/list/${genres.name}`)
}
const handleOnChange = (e) => {
// if(
// genres.id === movies.genres_ids
// )
setGenres(e.target.value)
}
// if(isFetching) return <Loader type='Loading films...'/>;
// if (error) return <Error />;
return (
<div className='flex flex-col'>.
<div className='w-full flex justify-between items-center sm:flex-row flex-col mt-4 mb-10'>.
<form onSubmit={handleOnSubmit}\>
<h2 className='font-bold text-3xl text-black text-left ml-4'>Discover</h2>
<select
value={currentGenre}
onChange={handleOnChange}
className='mt-4 ml-3'id='genres'\>
{genres.length > 0 &&genres.map((genre, i) => <option key={i} value={genres}>{genre.name}</option>)}
</select>
</form>
</div>
<div className='flex flex-wrap sm:justify-start justify-center gap-8'>
{movies.length > 0 &&
movies.map((movie) => <FilmCard key={movie.id} {...movie} />)}
</div>
</div>
)}
export default Discover
I would start by structuring the data fetch differently, something more compact..
Like:
export const useData = (url) => {
const [state, setState] = useState();
useEffect(() => {
const dataFetch = async () => {
const data = await (await fetch(url)).json();
setState(data);
};
dataFetch();
}, [url]);
return { data: state };
};
Maybe error handling too, like:
function App() {
const [state, setState] = useState([])
const [hasError, setHasError] = useState(false)
const {loading, setLoading} = useState(false)
useEffect(() => {
setLoading(true)
fetch("/api/data").then(
res => {
setState(res.data);
setLoading(false)}
).catch(err => {
setHasError(true))
setLoading(false)})
}, [])
return (
<>
{
loading ? <div>Loading...</div> : hasError ? <div>Error occured.</div> : (state.map( d => <div>{d}</div>))
}
</>
)
}
I hope it helps you ,
Cheers!

Trigger react useEffect

I'm fetching data from a firebase db it works when the component renders, but I can't make it to fetch again when there is a new entry in my db.
What I've tried
I've tried passing a state to the dependency array of useEffect and I changed that state every time my form was submitted (That's the time when there's a new entry in my db)
App
function App() {
const [showForm, setShowForm] = useState(true);
const [tasks, setTasks] = useState([]);
const [isSubmitted, setIsSubmitted] = useState(true);
//Fetch tasks from server
const fetchData = () => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json"
)
.then((response) => {
return response.json();
})
.then((data) => {
const tasks = [];
//Convert the data to an array so i can map over it
for (const key in data) {
const task = {
id: key,
...data[key],
};
tasks.push(task);
}
setTasks(tasks);
});
};
useEffect(() => {
fetchData();
}, [isSubmitted]);
//Show/Hide form
const onAddHandler = () => {
setShowForm(!showForm);
};
const formSubmitted = () => {
setIsSubmitted(!isSubmitted);
console.log(isSubmitted);
};
return (
<Container>
<Header click={onAddHandler} isShown={showForm}></Header>
{showForm ? <Form fs={formSubmitted}></Form> : ""}
<Tasks tasks={tasks}></Tasks>
</Container>
);
}
export default App;
Form
function Form(props) {
const [task, setTask] = useState();
const [dayTime, setDayTime] = useState();
const [reminder, setReminder] = useState();
//Posting Form data to firebase (DUMMY API)
const postFormData = (fullTask) => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json",
{
method: "POST",
body: JSON.stringify(fullTask),
headers: {
"Content-Type": "application/json",
},
}
);
};
//Make an object of form data
const onSubmit = (e) => {
e.preventDefault();
const fullTask = {
task: task,
dayTime: dayTime,
reminder: reminder,
};
//Post func call
postFormData(fullTask);
props.fs();
//Field clearing
setTask("");
setDayTime("");
setReminder("");
};
return (
<AddForm onSubmit={onSubmit}>
<FormControl>
<Label>Task</Label>
<Input
type="text"
placeholder="Add Task"
onChange={(e) => setTask(e.target.value)}
value={task}
required
></Input>
</FormControl>
<FormControl>
<Label>Day & Time</Label>
<Input
type="text"
placeholder="Add Task"
onChange={(e) => setDayTime(e.target.value)}
value={dayTime}
required
></Input>
</FormControl>
<FromControlCheck>
<CheckLabel>Set Reminder</CheckLabel>
<CheckInput
type="checkbox"
onChange={(e) => setReminder(e.currentTarget.checked)}
value={reminder}
></CheckInput>
</FromControlCheck>
<Submit type="submit" value="Save Task"></Submit>
</AddForm>
);
}
export default Form;
I would pass fetchData as a props to <Form>. When submitted, I would call it.
Form
const onSubmit = async (e) => {
e.preventDefault();
const fullTask = {
task: task,
dayTime: dayTime,
reminder: reminder,
};
//Post func call
await postFormData(fullTask);
await props.fetchData();
//Field clearing
setTask("");
setDayTime("");
setReminder("");
};
Then remove the isSubmitted state.
Try change the "Id" value to "id". Try make it the same name as the key for the id in "fecthData" function.
I think this solve your problem
function App() {
const [showForm, setShowForm] = useState(true);
const [tasks, setTasks] = useState([]);
const [isSubmitted, setIsSubmitted] = useState(false);
//Fetch tasks from server
const fetchData = () => {
fetch(
"https://react-task-tracker-8e519-default-rtdb.firebaseio.com/tasks.json"
)
.then((response) => {
return response.json();
})
.then((data) => {
const tasks = [];
//Convert the data to an array so i can map over it
for (const key in data) {
const task = {
id: key,
...data[key],
};
tasks.push(task);
}
setTasks(tasks);
});
};
useEffect(() => {
if (isSubmitted) {
fetchData();
setIsSubmitted(false);
}
}, [isSubmitted]);
//Show/Hide form
const onAddHandler = () => {
setShowForm(!showForm);
};
const formSubmitted = () => {
setIsSubmitted(true);
console.log(isSubmitted);
};
return (
<Container>
<Header click={onAddHandler} isShown={showForm}></Header>
{showForm ? <Form fs={formSubmitted}></Form> : ""}
<Tasks tasks={tasks}></Tasks>
</Container>
);
}
export default App;

ReactJS sending data to modal from map items

I have a map that render few items and i need when one element from map slected modal should load data about this selected items' id inside modal.
Like that:
<ListGroup>
{userinfo.map(item =>
(
<>
<ListGroup.Item key={item.id} onClick={handlePassInfoShow}>
{item.name}</ListGroup.Item>
</>
)
)}
</ListGroup>
<ModalPassInfo
modelClose={() => handlePassInfoClose()}
modelShow={showPaaInfo}
//id={item.id}
setshowPaaInfo={setshowPaaInfo}
/>
Here I am mapping through the user's array and adding a listgroup item to each of them with onClick modal. Now, whenever something is clicked inside map, the modal should be opened and read data about selected item.
And my modal like that.
const ModalPassInfo = ({modelShow, modelClose, id, showPaaInfo}) => {
const ref = React.createRef();
const [isError, setError] = useState(false);
const [isLoading, setLoading] = useState(true);
const [country_list, setCountries] = useState([]);
const [message, setMessage] = useState("");
const [data, setData] = useState({
//data about user
});
useEffect(() => {
loadNetwork();
}, []);
const loadNetwork = () => {
setLoading(true);
setError(false);
const selector = api.getItems("selector", {
tables: "country_list"
}).then(res => {
let response = res.data;
setCountries(response.country_list);
});
const data = api.getItems(`user-info/${id}`, {
}).then(res => {
let response = res.data;
setData(response);
});
Promise.all([selector, data]).then(res => {
console.log(res);
setError(false);
setLoading(false);
}).catch(e => {
console.log(e);
setMessage(e.toString());
setLoading(false);
setError(true);
});
};
const onRefresh = () => {
loadNetwork();
};
if (isError) {
return <ErrorMessage message={message} onRefresh={onRefresh}/>
}
if (isLoading) {
return <Loader/>
}
If I go to the page, the modal is loading immediately. And during onClick, only the last item id is retrieved.
And moy consts
const [showPaaInfo, setshowPaaInfo] = useState(false);
const handlePassInfoClose = () => setshowPaaInfo(false);
const handlePassInfoShow = () => {
setshowPaaInfo(true)
};
My question is. Any item on the map should send an id to the modal when the item is clicked. Where am I wrong?
Define one state
const [show, setShow] = React.useState(false);
function
const handlePassInfoShow = (data){
setShow(true);
console.log(data);
}
Change this to
<ListGroup>
{userinfo.map(item =>
(
<>
<ListGroup.Item key={item.id} onClick={()=>handlePassInfoShow(item)}>
{item.name}</ListGroup.Item>
</>
)
)}
</ListGroup>
{show && ( <ModalPassInfo
modelClose={() => handlePassInfoClose()}
modelShow={showPaaInfo}
//id={item.id}
setshowPaaInfo={setshowPaaInfo}
/>
)}

React Load More using Hooks

I try to do Load More on a list of data as written below:
import React, { useState, useEffect } from "react";
import { render } from "react-dom";
import axios from "axios";
import "./style.css";
const App = () => {
const LIMIT = 2;
const [data, setData] = useState([]);
const [isLoading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const loadData = async (skip = 1, limit = LIMIT) => {
const URL = "https://reqres.in/api/users";
const headers = {
"Content-Type": "application/json",
Accept: "application/json"
};
const params = {
page: skip,
per_page: limit
};
const a = await axios.get(URL, { params, headers });
// const b = [...new Set([...data, ...a.data.data])]; <-- setting this will thrown error
setData(a.data.data);
setLoading(false);
};
useEffect(() => {
setLoading(true);
loadData(page);
}, [page]);
useEffect(() => {
console.log("page", page, "data", data.length);
}, [page, data]);
const doReset = evt => {
evt.preventDefault();
setPage(1);
};
const doLoadMore = evt => {
evt.preventDefault();
setPage(page + 1);
};
return (
<div className="container">
<h1>Listing</h1>
<button className="btn text-primary" onClick={evt => doReset(evt)}>
Reset
</button>
<button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
Load More
</button>
{isLoading && <p>Loading..</p>}
{!isLoading && (
<ul>
{data.map(a => (
<li key={a.id}>
{a.id}. {a.email}
</li>
))}
</ul>
)}
</div>
);
};
render(<App />, document.getElementById("root"));
a fully working example in here.
i think this code should be working, but is not.
const a = await axios.get(URL, { params, headers });
const b = [...new Set([...data, ...a.data.data])];
setData(b);
so please help, how to do Load More in React Hooks?
after a few try, i think this is the best thing i can do. make the code working but also not let the compiler warning:
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import Navbar from "./Navbar";
const App = () => {
const LIMIT = 2;
const [tube, setTube] = useState([]);
const [data, setData] = useState([]);
const [isLoading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const loadData = useCallback(
async (limit = LIMIT) => {
setLoading(true);
const URL = "https://reqres.in/api/users";
const headers = {
"Content-Type": "application/json",
Accept: "application/json"
};
const params = {
page,
per_page: limit
};
const a = await axios.get(URL, { params, headers });
if (!a.data.data) {
return;
}
setData(a.data.data);
setLoading(false);
},
[page]
);
useEffect(() => {
if (!isLoading) {
return;
}
setTube([...new Set([...tube, ...data])]);
}, [data, isLoading, tube]);
useEffect(() => {
loadData();
}, [loadData]);
useEffect(() => {
console.log("page", page, "data", data.length);
}, [page, data]);
const doLoadMore = evt => {
evt.preventDefault();
setPage(page + 1);
};
return (
<>
<Navbar />
<main role="main" className="container">
<div className="starter-template text-left">
<h1>Listing</h1>
<button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
Load More
</button>
<ul>
{tube &&
tube.map(a => (
<li key={a.id}>
{a.id}. {a.email}
</li>
))}
</ul>
{isLoading && <p>Loading..</p>}
</div>
</main>
</>
);
};
export default App;
also i found, it could be much easier just apply this eslint-disable-next-line react-hooks/exhaustive-deps to let the compiler ignore the warning. something like this.
useEffect(() => {
setConfig({...config, params: {...params, skip}});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [skip]);
for information can be found on this:
how-to-fix-missing-dependency-warning-when-using-useeffect-react-hook
https://stackoverflow.com/a/55844055/492593
react #14920
I got your example to work by changing to this:
const b = [...data, ...a.data.data];
setData(b);

Resources