0

The calculateTotalPages function below calculates the total pages that will be displayed. Initial value for totalPages state is set to 0. Where should I call setState to update the totalPages state once the calculateTotalPages function has calculated and returned the value of total pages? I know setState is not to be called within render().

class Items extends Component {
  state = {
    items: ['item1', 'item2', 'item3', 'item4', 'item5', 'item6', 'item7'],
    isLoading: false,
    currentPage: 1,
    itemsPerPage: 3,
    totalPages: 0
  }

  calculateTotalPages = (items, itemsPerPage) =>{
    let getTotalPages;
    const quotient = Math.trunc(items.length / itemsPerPage);
    const remainder = items.length % itemsPerPage;

    if(remainder === 0){
      getTotalPages = quotient;
    }else{
      getTotalPages = quotient + remainder;
    }

    return getTotalPages;
  };

  
  render() {
    const{items, totalPages, currentPage, itemsPerPage} = this.state;
    const getTotalPages = this.calculateTotalPages(items, itemsPerPage)

    console.log(getTotalPages)

    return (
         <div><h1>{getTotalPages}</h1></div>
    )
  }
}

export default Items;
5
  • I'd probably do it in componentDidMount if possible, and if it only needs to be set once Commented Jan 15, 2021 at 20:53
  • This doesn't address your question directly but I want to provide a strong suggestion. Don't store derived data, like total pages, in your component state, this is also considered an anti-pattern. Thinking in React. The total pages is a simple calculation from the items and itemsPerPage state values. Commented Jan 15, 2021 at 23:51
  • 1
    Drew, so basically I should simply call the calculateTotalPages function within render and get the value? No need to store it in a state? Commented Jan 16, 2021 at 2:08
  • 1
    That is exactly correct. Commented Jan 16, 2021 at 3:02
  • Thank you. That makes so much sense and I have corrected my code. Commented Jan 16, 2021 at 3:11

2 Answers 2

1

render function should be pure.

The render() function should be pure, meaning that it does not modify component state, it returns the same result each time it’s invoked, and it does not directly interact with the browser.

Don't call this in render just call in componentDidmount

componentDidMount() {
this.calculateTotalPages(this.state.items, this.state.itemsPerPage)
}

From the docs

You may call setState() immediately in componentDidMount(). It will trigger an extra rendering, but it will happen before the browser updates the screen. This guarantees that even though the render() will be called twice in this case, the user won’t see the intermediate state.

Then set the state inside the function would be easier.

  calculateTotalPages = (items, itemsPerPage) =>{
    let getTotalPages;
    const quotient = Math.trunc(items.length / itemsPerPage);
    const remainder = items.length % itemsPerPage;

    if(remainder === 0){
      getTotalPages = quotient;
    }else{
      getTotalPages = quotient + remainder;
    }

    this.setState({totalPages:getTotalPages}) //set state here

    return getTotalPages;
  };

Sign up to request clarification or add additional context in comments.

3 Comments

Makes sense. Thanks! Can I also use an if statement in componentDidMount to check if the state value has changed from the previous one in order avoid executing that function more than once?
ComponentDidMount will only call once. but as we are setting the state in the ComponentDidMount method, technically render method gets called twice.
1

In a class based component you can do this in componentDidMount() since this is invoked only once initially after the first render.

In a functional component you can use useEffect() Hook with empty array as the second argument.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.