Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
263 views
in Technique[技术] by (71.8m points)

node.js - Creating a reusable pagination in React, Express, Mongodb

I created a pagination component inside my app:

const Paginate = ({ pages, page, keywordInput = '' }) => {
    return pages > 1 && (
        <>
            <nav aria-label="Page navigation example">
            <ul className="pagination">
                {[...Array(pages).keys()].map(x => (
                   <li className={`page-item ${x+1 === page ? 'active' : ''} `} key={x + 1}>
                       <Link to={keywordInput ? `/search/${keywordInput}/page/${x+1}` : `/page/${x+1}`} className="page-link">{x+1}</Link>
                  </li>
                )) }

               
            </ul>
            </nav>
        </>
    )
}

On my homepage that has the search bar this will work as it will go to the search result page and will display 6 items per page. So I tried to customize this on my controller so I can use this on my admin backend and can add the pagination on each listing backend pages.

// @description   Fetch all resorts
// @route         GET /api/resorts
// @access        Public
const getResorts = expressAsyncHandler(async (req, res) => {
    const pageSize = 6
    const page = Number(req.query.pageNumber) || 1

    const keyword = req.query.keyword ? {
        name: {
            $regex: req.query.keyword,
            $options: 'i'
        }
    } : {}

    const count = await Resort.countDocuments({ ...keyword } )
    const resorts = await Resort.find({ ...keyword }).limit(pageSize).skip(pageSize * (page - 1))
    res.json({ resorts, page, pages: Math.ceil(count / pageSize) })
})

// @description   Fetch all resorts created by the resort owner
// @route         GET /api/resorts
// @access        Public
const getOwnerResorts = expressAsyncHandler(async (req, res) => {
    const userId = req.params.userid
    const pageSize = 6
    const page = Number(req.query.pageNumber) || 1
    const count = await Resort.countDocuments({ user: userId } )
    const resorts = await Resort.find({ user: userId }).limit(pageSize).skip(pageSize * (page - 1))
    res.json({ resorts, page, pages: Math.ceil(count / pageSize) })
})

Now on my react frontend I created a route on my App.js that will point page to page without the keyword on my backend:

     <Route path='/admin/resortslist' component={ResortListAdminScreen} exact />
     <Route path='/admin/resortslist/page/:pageNumber' component={ResortListAdminScreen} exact />

And then on my admin access - ResortListAdminScreen.js, I tried to call the paginate component above:

const ResortListAdminScreen = ({ history }) => {
    const dispatch = useDispatch()

    const resortList = useSelector(state => state.resortList)
    const { loading, error, resorts, page, pages } = resortList

    const resortDelete = useSelector(state => state.resortDelete)
    const { loading:loadingDelete, error:errorDelete, success:successDelete } = resortDelete

    const userLogin = useSelector(state => state.userLogin)
    const { userInfo } = userLogin

    const resortCreate = useSelector(state => state.resortCreate)
    const { 
        loading:loadingCreate, 
        error:errorCreate, 
        success:successCreate,
        resort:createdResort 
    } = resortCreate

    useEffect(() => {

        if(userInfo.role !== 'administrator'){
            history.push('/')
        } 

         dispatch(listResorts())

        
    }, [dispatch, history, userInfo, successDelete, successCreate, createdResort])


    const deleteHandler = (id) => {
        if(window.confirm('Are you sure')){
            dispatch(deleteResort(id))
        }
    }

    return (
        <>

    <h1>Resorts</h1>
     { loadingDelete && <Loader />}      
    { errorDelete && <Message variant='danger'>{errorDelete}</Message>}    
    { loadingCreate && <Loader />}      
    { errorCreate && <Message variant='danger'>{errorCreate}</Message>}    
    { loading ? <Loader /> : error ? <Message variant='danger'>{error}</Message> : (
    
    <>
       <Link to='/admin/resorts/create'>Create Resort</Link>
       <table className="table">
        <thead className="thead-dark">
          <tr>
            <th scope="col">Id</th>
            <th scope="col">Name</th>
            <th scope="col">Price Per Night</th>
            <th scope="col">Description</th>
            <th scope="col">Address</th>
            <th scope="col">Phone</th>
            <th scope="col">Website</th>
            <th scope="col">Amenities</th>
            <th scope="col"></th>
          </tr>
        </thead>
        <tbody>
           {resorts.map(resort => (
               <tr key={resort._id}>
                  <td>{resort._id}</td>
                  <td>{resort.name}</td>
                  <td>{resort.price_per_night}</td>
                  <td>{resort.description}</td>
                  <td>{`${resort.address}, ${resort.city}, ${resort.province}, Philippines ${resort.zip_code}`}</td>
                  <td>{resort.phone}</td>
                  <td>{resort.website}</td>
                  <td>{
                  Object.entries(resort.amenities).map(
                    ([key, value], index, arr) => {
                        return (value && index === arr.length-1) ? <span>{key}</span> : value ? <span>{`${key}, `}</span> : null;
                    })
                 }</td>
                  <td>

                  <Link to={`/admin/resort/${resort._id}/edit`}>
                  <button classNameName="btn btn-sm">
                      EDIT
                  </button>
                  </Link>
      
                   <button classNameName="btn btn-sm" onClick={() => deleteHandler(resort._id)}>
                      DELETE
                  </button>
                  </td>
               </tr>
           ))}
        </tbody>
      </table>
      </>
    )}
        <Paginate pages={pages} page={page}  />
        </>
    )
}

export default ResortListAdminScreen

Notice that I added the <Paginate pages={pages} page={page} /> at the bottom. When I click on each page number it returns http://localhost:3000/page/1 and the screen is blank. I also tried visiting http://localhost:3000/admin/resortsList/page/1 but it doesn't show the next page list instead it is still stuck on the first page (which is the first 6 items).

Not sure how to fix this, but I would want to customize the paginate component and be able to reuse it on my component. Any idea how to attain this so I can add this on my backend listing? Should I duplicate the Paginate component or it can be customize?

Note: On the admin access backend we don't need a search but only a normal pagination


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
等待大神答复

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...