Warm tip: This article is reproduced from stackoverflow.com, please click
reactjs react-hooks use-effect

React Hook useEffect() run continuously although I pass the second params

发布于 2020-03-27 15:39:50

I have problem with this code

If I pass the whole pagination object to the second parameters of useEffect() function, then fetchData() will call continuously. If I only pass pagination.current_page so It will call only one time, but when I set new pagination as you see in navigatePage() function, the useEffect() does not call to fetchData() although pagination has changed.

How to solve this. Thank you very much!

Besides I do not want the use useEffect() call when first time component mounted because the items is received from props (It is fetch by server, this is nextjs project).

import React, {useEffect, useState} from 'react';
import Filter from "../Filter/Filter";
import AdsListingItem from "../AdsListingItem/AdsListingItem";
import {Pagination} from "antd-mobile";

import styles from './AdsListing.module.css';

import axios from 'axios';

const locale = {
    prevText: 'Trang trước',
    nextText: 'Trang sau'
};

const AdsListing = ({items, meta}) => {

    const [data, setData] = useState(items);

    const [pagination, setPagination] = useState(meta);

    const {last_page, current_page} = pagination;

    const fetchData = async (params = {}) => {
        axios.get('/ads', {...params})
            .then(({data}) => {
                setData(data.data);
                setPagination(data.meta);
            })
            .catch(error => console.log(error))
    };

    useEffect( () => {
        fetchData({page: pagination.current_page});
    }, [pagination.current_page]);

    const navigatePage = (pager) => {
        const newPagination = pagination;
        newPagination.current_page = pager;
        setPagination(newPagination);
    };

    return (
        <>
            <Filter/>
            <div className="row  no-gutters">
                <div className="col-md-8">
                    <div>
                        {data.map(item => (
                            <AdsListingItem key={item.id} item={item}/>
                        ))}
                    </div>
                    <div className={styles.pagination__container}>
                        <Pagination onChange={navigatePage} total={last_page} current={current_page} locale={locale}/>
                    </div>
                </div>
                <div className="col-md-4" style={{padding: '15px'}}>
                    <img style={{width: '100%'}} src="https://tpc.googlesyndication.com/simgad/10559698493288182074"
                         alt="ads"/>
                </div>
            </div>


        </>
    )
};

export default AdsListing;
Questioner
Jeffrey K
Viewed
20
Drew Reese 2020-01-31 16:14

The issue is you aren't returning a new object reference. You save a reference to the last state object, mutate a property on it, and save it again.

const navigatePage = (pager) => {
  const newPagination = pagination; // copy ref pointing to pagination
  newPagination.current_page = pager; // mutate property on ref
  setPagination(newPagination); // save ref still pointing to pagination
};

In this case the location in memory that is pagination remains static. You should instead copy all the pagination properties into a new object.

const navigatePage = (pager) => {
  const newPagination = {...pagination}; // shallow copy into new object
  newPagination.current_page = pager;
  setPagination(newPagination); // save new object
};

To take it a step further you really should be doing functional updates in order to correctly queue up updates. This is in the case that setPagination is called multiple times during a single render cycle.

const navigatePage = (pager) => {
  setPagination(prevPagination => {
    const newPagination = {...prevPagination};
    newPagination.current_page = pager;
  });
};

In the case of pagination queueing updates may not be an issue (last current page set wins the next render battle), but if any state updates actually depend on a previous value then definitely use the functional update pattern,