简体   繁体   中英

How can I setState from inside a render function in react

i know you are supposed to keep render functions pure but I have a special case where I need to pass some values and update the state inside a render function but i am getting the following error message:

Cannot update during an existing state transition (such as within render ). Render methods should be a pure function of props and state

Code:

import Carousel from 'react-elastic-carousel';
import top from '../images/top.png';
import ArrowRight from '../images/arrowRight.svg';
import React, {Component} from 'react';
class contentSlider extends Component {

  state = {
    disabled: '',
    leftArrow: false,
    rightArrow: true
  }

  sliderData = [
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    },
    {
      title: 'POLO',
      typeOfcontent: 'Mens T-Shirt',
      rrp: '£105',
      ourPrice: '£55',
      salePrice: '£45',
      image: top
    }

  ]

  breakPoints = [
    { width: 2, itemsToShow: 2, itemsToScroll: 2 },
    { width: 550, itemsToShow: 3, itemsToScroll: 3},
    { width: 850, itemsToShow: 4, itemsToScroll: 3 },
    { width: 970, itemsToShow: 4, itemsToScroll: 3 },
    { width: 1150, itemsToShow: 5, itemsToScroll: 3 },
  ] 

  setDirection = (slideDirection) => {
    switch(slideDirection) {
      case "next":
        this.carousel.slideNext();
        let slideNext = document.getElementById('slider-move');
        
        if(slideNext.classList.contains('test-right')) {
          slideNext.classList.remove('test-right');
          slideNext.classList.add('test-left');
        }
            
      break;
      case "previous":
        this.carousel.slidePrev();
        let slidePrevious = document.getElementById('slider-move');
        
        if(slidePrevious.classList.contains('test-left')) {
          slidePrevious.classList.remove('test-left');
          slidePrevious.classList.add('test-right');
        }

      break;
    }
  }

  getAmountOfPages = (pages, activePage ) => {
  console.log(activePage)

  let firstItem = pages[0];
  let [lastItem] = pages.slice(-1);

  if(firstItem === activePage) {
    this.setState({
      leftArrow: false,
      rightArrow: true,
    })
  } else if(lastItem === activePage) {
    this.setState({
      leftArrow: true,
      rightArrow: false,
    })
  } else {
    this.setState({
      leftArrow: true,
      rightArrow: false,
    })
  }

// get first item in the array and compare it to the active page

// get the last element in the array and compare it to the active page
  }

  render() {
    return (
      <div className="content-slider-wrapper">
        <div className="content-slider-title">
          <span>PRODUCTS OF THE WEEK</span>
        </div>
        <div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} arrow-container`}>
          <img onClick={() => this.setDirection("previous")} className="arrow-left" src={ArrowRight} />
          <img onClick={() => this.setDirection("next")} src={ArrowRight} />   
        </div>

        <div className={`${this.sliderData.length === 5 ? 'mt-xl-5' : ''} content-slider-container`}> 
        <div className="test-right" id="slider-move">
          
          
          <Carousel 
              ref={ref => (this.carousel = ref)}
              breakPoints={this.breakPoints} 
              disableArrowsOnEnd={true}
              renderPagination={({ pages, activePage, onClick }) => {
                this.getAmountOfPages(pages, activePage);
                return (
                  <div className={`${this.sliderData.length === 5 ? 'd-xl-none' : ''} black-slider-container`}>
                    {pages.map(page => {
                      const isActivePage = activePage === page
                      return (
                        <div className={isActivePage ? 'black-slider' : 'blank-slider'}
                          key={page}
                          onClick={() => onClick(page)}
                          active={isActivePage}
                        />
                      )
                    })}
                  </div>
                )
              }}
              >
              {this.sliderData.map((item, index) => (
                <div key={index} className="carousel-item-container">
                  <div className="carousel-image-container">
                    <img src={top} />
                  </div>
                  <div className="carousel-text-container">
                    <ul>
                      <li className="carousel-text-container-title">{item.title}</li>
                      <li className="carousel-text-container-text">{item.typeOfProduct}</li>
                      <li className="carousel-text-container-text line-through">RRP {item.rrp}</li>
                      <li className="carousel-text-container-text line-through">Our Price: {item.ourPrice}</li>
                      <li className="carousel-text-container-text">Sale Price: {item.salePrice}</li>
                    </ul>
                  </div>
                </div>
              ))}
            </Carousel>
          </div>
          </div>
      </div>
    )
  }
}

export default contentSlider;

I need to call this function

this.getAmountOfPages(pages, activePage);

which is inside my render to update the state when needed however it dont work any ideas....

I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.

By calling this function the way you are trying to do it, you would fall into an infinite loop. Look:

  1. You update the state inside the render () method;
  2. The render () method is called due to the state update;
  3. The render method updates the state;
  4. all over again

I've already used this library, so I'll show you how I did it:

      <Carousel
          itemsToShow={7}
          renderArrow={renderArrow}
          enableMouseSwipe={false}
          pagination={false}
          breakPoints={[
            {
              width: 450,
              itemsToShow: 3,
              enableSwipe: true,
              enableMouseSwipe: true,
              itemsToScroll: 3,
              renderArrow: () => <div />,
            },
            {
              width: 500,
              itemsToShow: 3,
            },
            { width: 620, itemsToShow: 4 },
            { width: 720, itemsToShow: 5 },
            { width: 850, itemsToShow: 6 },
            { width: 1150, itemsToShow: 7 },
          ]}
        >
          {products &&
            products.map((p) => (
              <div key={p.docId} className="products-prod">
                <img
                  onClick={() => addProductCart(p)}
                  alt={p.descricao}
                  src={`data:image/png;base64,${p.thumbnail}`}
                  className={`shadow2 ${
                    !cart.includes(p.docId) && adding.includes(p.docId)
                      ? `pulse`
                      : ``
                  }`}
                />
                <section onClick={() => addProductCart(p)}>
                  {cart.includes(p.docId) ? (
                    <img src={ICONS.shoppingFilled} />
                  ) : (
                    <img src={ICONS.shoppingOutlined} />
                  )}
                </section>
                <article>
                  <h5>R$</h5>
                  {p.preco && formatCash(p.preco, true)}
                </article>
              </div>
            ))}
        </Carousel>

And:

  const renderArrow = ({ onClick, type, isEdge: noMoreItems }) => (
    <Button
      onClick={onClick}
      style={{
        margin: "auto 0",
        borderRadius: "100px",
        background: COLORS.primary,
        borderColor: COLORS.primary,
        width: "30px",
        minWidth: "30px",
        height: "30px",
        minHeight: "30px",
        padding: 0,
        display: "flex",
        justifyContent: "center",
        alignItems: "center",
      }}
      type="primary"
    >
      {type === consts.PREV ? (
        <CaretLeftOutlined style={{ margin: "2px 2px 0 0" }} />
      ) : (
        <CaretRightOutlined style={{ margin: "2px 0 0 2px" }} />
      )}
    </Button>
  );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM