Skip to content

Instantly share code, notes, and snippets.

@kuldeepkeshwar
Last active February 9, 2021 21:08
Show Gist options
  • Save kuldeepkeshwar/f0f913d945c7840a24eabc91feef199e to your computer and use it in GitHub Desktop.
Save kuldeepkeshwar/f0f913d945c7840a24eabc91feef199e to your computer and use it in GitHub Desktop.
import React,{ useReducer, useEffect, useRef } from "react";
import axios from "axios";
const initialState = {
items: [],
loading: false,
error: null,
filter: null,
pagination: {
limit: 5,
offset: 0,
totalRecords: 0
}
};
// Our reducer function to handle state changes based on action
function reducer(state, { type, payload }) {
switch (type) {
case "filter": {
const { filter } = payload;
return {
...state,
pagination: {
limit: state.pagination.limit,
offset: 0,
totalRecords: 0
},
filter
};
}
case "fetch_start": {
return {
...state,
loading: true,
error: null
};
}
case "fetch_success": {
const { items, totalRecords } = payload;
const { limit, offset } = state.pagination;
return {
...state,
items,
loading: false,
pagination: {
limit: limit,
offset: offset,
totalRecords: totalRecords
}
};
}
case "fetch_failure": {
const { error } = payload;
return {
...state,
items: [],
loading: false,
error
};
}
case "next": {
const { limit, offset, totalRecords } = state.pagination;
if (offset + limit < totalRecords) {
return {
...state,
pagination: {
limit: limit,
offset: offset + limit,
totalRecords: totalRecords
}
};
} else {
throw new Error("offset + limit should be less than totalRecords");
}
}
case "previous": {
const { limit, offset, totalRecords } = state.pagination;
if (offset - limit < 0) {
throw new Error("offset should be greater than/equal Zero");
}
return {
...state,
pagination: {
limit: limit,
offset: offset - limit,
totalRecords: totalRecords
}
};
}
default:
throw new Error("Missing action type");
}
}
//hook
function useTable({ url, initialState: { filter, limit } }) {
const [state, dispatch] = useReducer(reducer, {
...initialState,
filter,
pagination: { ...initialState.pagination, limit }
});
useEffect(fetchItems, [state.filter, state.pagination.offset]);
function setFilter(filter) {
dispatch({ type: "filter", payload: { filter } });
}
function next() {
dispatch({ type: "next" });
}
function previous() {
dispatch({ type: "previous" });
}
function fetchItems() {
const {
filter,
pagination: { limit, offset }
} = state;
const params = { filter, limit, offset };
dispatch({ type: "fetch_start" });
axios
.get(url, { params })
.then(function(resp) {
const { items, totalRecords } = resp.data;
dispatch({ type: "fetch_success", payload: { items, totalRecords } });
})
.catch(function(err) {
const error = err.response.data;
dispatch({ type: "fetch_failure", payload: { error } });
});
}
return { state, next, previous, setFilter };
}
//App
function App() {
const {
state: { items, loading, error, filter, pagination: { totalRecords, offset, limit } },
next,
previous,
setFilter,
} = useTable({
url: "/users",
initialState: { filter: { status: undefined }, limit: 10 }
});
const isPrevBtnDisabled = offset === 0;
const isNextBtnDisabled = !(offset + limit < totalRecords);
const prevBtnCls = `btn pagination-btn ${
isPrevBtnDisabled ? "disabled" : ""
}`;
const nextBtnCls = `btn pagination-btn ${
isNextBtnDisabled ? "disabled" : ""
}`;
return (
<div className="App">
<div>
<button
className={`btn link ${filter.status === undefined ? "active" : ""}`}
onClick={() => setFilter({ status: undefined })}
>
All
</button>
<button
className={`btn link ${filter.status === "active" ? "active" : ""}`}
onClick={() => setFilter({ status: "active" })}
>
Active
</button>
<button
className={`btn link ${filter.status === "expired" ? "active" : ""}`}
onClick={() => setFilter({ status: "expired" })}
>
Expired
</button>
</div>
<div className="list">
<div className="row header">
<div className="cell">ID</div>
<div className="cell">NAME</div>
<div className="cell">STATUS</div>
</div>
{loading ? (
<div className="grow" >...</div>
) : error ? (
<div>{error}</div>
) : (
<div>
{items.map(item => {
return (
<div className="row" key={item.id}>
<div className="cell">{item.id}</div>
<div className="cell">{item.name}</div>
<div className="cell">{item.status}</div>
</div>
);
})}
</div>
)}
</div>
<div className="footer">
<button
className={prevBtnCls}
onClick={() => !isPrevBtnDisabled && previous()}
>
{"<"}
</button>
<button
className={nextBtnCls}
onClick={() => !isNextBtnDisabled && next()}
>
{">"}
</button>
</div>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment