Search code examples
javascriptreactjsstatelifecycle

Disable child's button from parent conditionally - React


I have a Paginator child component and I want to disable the left arrow button if it's on the first page and make it clickable again if I'm above the first page. The same thing with the right arrow button but with page number 5.

Here is the condition that I tried to write for this solution:

if (this.state.page <= 1) {
  this.setState({
    disabledPrev: true
  })
} else {
  this.setState({
    disabledPrev: false
  })
}

if (this.state.page >= 5) {
  this.setState({
    disabledNext: true
  })
} else {
  this.setState({
    disabledNext: false
  })
}

But it doesn't work, no matter where I put it. I think the problem is in the wrong usage of lifecycle methods.

Here is the whole code:

App.js

import React from 'react';
import './App.css';

import Loading from './components/Loading/Loading';
import Header from './components/Header/Header';
import Table from './components/Table/Table';
import Paginator from './components/Paginator/Paginator';

const BASE_URL = 'https://api.udilia.com/coins/v1/'
export default class App extends React.Component{ 
  constructor(props) {
    super(props);
    console.log('constructor');
    this.state = {
      currencies: null,
      isDataReady: false,
      page : 1,
      disabledPrev: false,
      disabledNext: false,
    }
  }

  fetchCurrencies = (pageNumber) => {
    fetch(`${BASE_URL}cryptocurrencies?page=${pageNumber}&perPage=20`)
      .then(res => res.json())
      .then(json => json.currencies)
      .then(currencies => {
        this.setState({
          currencies: currencies,
          isDataReady: true,
        })
      });
  }

  UNSAFE_componentWillMount() {
    console.log('will mount', this.state.page);

  }

  componentDidUpdate() {
    console.log('didUpdate', this.state.page);

  }

  componentDidMount() {
    console.log('did mount', this.state.page);

    //Here is the conditions
    if (this.state.page <= 1) {
      this.setState({
        disabledPrev: true
      })
    } else {
      this.setState({
        disabledPrev: false
      })
    }

    if (this.state.page >= 5) {
      this.setState({
        disabledNext: true
      })
    } else {
      this.setState({
        disabledNext: false
      })
    }

    const {page} = this.state;

    this.fetchCurrencies(page);

  }

  //here is the event handler, I change the page value from here
  handlePaginationClick  = (direction) => () => {
    console.log('clicked', this.state.page);

    let page = direction === 'next' ? this.state.page + 1 : this.state.page - 1;

    this.setState({page})
    this.fetchCurrencies(page);
  }

  render(){
    console.log('render', this.state.page);

    return (
        !this.state.isDataReady
          ? <Loading />
          : <div className="App">
              <Header />
              <Table
                data={this.state.currencies}
              />
              <Paginator 
                prev={this.handlePaginationClick('prev')}
                next={this.handlePaginationClick('next')}
                page={this.state.page}
                disabledPrev={this.state.disabledPrev}
                disabledNext={this.state.disabledNext}
              />

            </div>
    );
  }
}

Paginator.js

import React from 'react';

export default function Paginator(props) {
    return(
        <div>
            <button disabled={props.disabledPrev} onClick={() => props.prev()}> &larr; </button>
            <span>page {props.page} of 10</span>
            <button disabled={props.disabledNext} onClick={() => props.next()}> &rarr; </button>
        </div>
    )
}

Solution

  • Try this

    import React from 'react';
    import './App.css';
    
    import Loading from './components/Loading/Loading';
    import Header from './components/Header/Header';
    import Table from './components/Table/Table';
    import Paginator from './components/Paginator/Paginator';
    
    const BASE_URL = 'https://api.udilia.com/coins/v1/'
    export default class App extends React.Component{ 
      constructor(props) {
        super(props);
        console.log('constructor');
        this.state = {
          currencies: null,
          isDataReady: false,
          page : 1
        }
      }
    
      fetchCurrencies = (pageNumber) => {
        fetch(`${BASE_URL}cryptocurrencies?page=${pageNumber}&perPage=20`)
          .then(res => res.json())
          .then(json => json.currencies)
          .then(currencies => {
            this.setState({
              currencies: currencies,
              isDataReady: true,
            })
          });
      }
    
      UNSAFE_componentWillMount() {
        console.log('will mount', this.state.page);
    
      }
    
      componentDidUpdate() {
        console.log('didUpdate', this.state.page);
    
      }
    
      componentDidMount() {
        console.log('did mount', this.state.page);
        const {page} = this.state;
        this.fetchCurrencies(page);
    
      }
    
      //here is the event handler, I change the page value from here
      handlePaginationClick  = (direction) => () => {
        console.log('clicked', this.state.page);
    
        let page = direction === 'next' ? this.state.page + 1 : this.state.page - 1;
    
        this.setState({page})
        this.fetchCurrencies(page);
      }
    
      render(){
        console.log('render', this.state.page);
        const {page} = this.state;
        const disabledPrev = page <=1 ? true : false;
        const disabledNext = page >=5 ? true : false;
        return (
            !this.state.isDataReady
              ? <Loading />
              : <div className="App">
                  <Header />
                  <Table
                    data={this.state.currencies}
                  />
                  <Paginator 
                    prev={this.handlePaginationClick('prev')}
                    next={this.handlePaginationClick('next')}
                    page={page}
                    disabledPrev={disabledPrev}
                    disabledNext={disabledNext}
                  />
    
                </div>
        );
      }
    }