Реагировать на порядок методов жизненного цикла - PullRequest
0 голосов
/ 12 мая 2018

У меня есть бесконечное слайд-шоу в моем проекте, я не уверен, что мои методы в правильном порядке.Я вызываю функцию извлечения для componentWillMount(), а затем использую эти данные для componentDidMount() ..

Проблема может быть в чем-то другом, но раньше это работало, но теперь это не ..

      componentWillMount() {
    this.props.getAdverts();
  }

  componentDidMount() {
      var index = 0;
      setInterval(() => {
        this.setState({
          bg:  this.props.adverts ? this.props.adverts[index++].photo: this.state.bg,
          text:  this.props.adverts ? this.props.adverts[index++].text: this.state.text
          })
          if(index === this.props.adverts.length) index = 0;
        }, 4000) 
      }

Когда я регистрирую this.props.adverts, это массив ..

Ошибка: Cannot read property 'photo' of undefined или Cannot read property 'text' of undefined

STACKBLITZ ссылка: https://stackblitz.com/edit/react-sshop

1 Ответ

0 голосов
/ 13 мая 2018

Это пример того, как вы можете сделать это, используя текущий код.Я не согласен с тем, как это делается, но для начала это должно помочь вам начать исследовать более «реактивный» способ кодирования.

// This is a composable function, we will pass it in to setInterval.
// we need access to the component, so we will pass it in and then
// return the function signature that setInterval wants to call
const changeAdvertComposer = function changeAdvertComposer(component) {
     // we start at -1 so the first call asks for 0
     let index = -1;

     // return the function that setInterval will call
    return function changeAdvert() {
          const adverts = component.props.adverts;
          if (!adverts || !adverts.length) {
               // we have no advertisements so lets exit
               return;
          }
          index++;
          // reset our index if needed
          if (index >= adverts.length) {
              index = 0;
          }
          // increment and grab our object
          const advert = adverts[index];

          // grab our state or a default failover structure
          const state = component.state.advert || { bg: '', text: '' };
          // set our state
          component.setState({
              advert: {
                   bg: advert.photo || state.bg,
                   text: advert.text || state.text,
              }
          });
     }
};

export ExampleAdvertManager extends Component {

    // setup some validations on our props
    static propTypes = {
         getAdverts: PropTypes.func.isRequired,
         adverts: PropTypes.arrayOf(PropTypes.shape({
              photo: PropTypes.string,
              text: PropTypes.string
         }))
    }

    constructor(props) {
         super(props);

         // we will store the state in the interval object so we can
         // check for null (simple way to find out if loading)
         this.state = {
              advert: null
         };

         // we will store the ref to our interval here
         this._intervalRef = null;
    }

    componentDidMount() {
         // we are loaded let's call our data action loader
         this.props.getAdverts();
    }

    componentWillUpdate() {
        // do we have any ads?
        const adlength = this.props.adverts ? this.props.adverts.length : 0;

        if (adlength && !this._intervalRef) {
             // we have ads and we have not setup the interval so lets do it
             this._intervalRef = setInterval(changeAdvertComposer(this), 4000);
        } else if (!adlength && this._intervalRef) {
             // we have no ads but we have an interval so lets stop it
             clearInterval(this._intervalRef);
             this._intervalRef = null;
        }
    }

    componentWillUnmount() {
         // we are unloading, lets clear up the interval so we don't leak
         if (this._intervalRef) {
               clearInterval(this._intervalRef);
               this._intervalRef = null;
         }
    }

    render() {
        if (this.stage.advert) {
            // render here
        }
        // we don't have data yet
        return null; // or some loading view
    }
}

И, возможно, в этом примере я перешел за борт, ямы занимаемся этим слишком долго и действительно зависим от возможности компоновки для модульного тестирования.Мне трудно не думать таким образом.Я не сделал setState компонуемым, хотя ... кроличья нора идет намного глубже.

Действительно, я бы сделал интервальный таймер своим собственным компонентом, который рендерится нулем и запускает обратные вызовы для моего компонента.Просто делает все чище.Это будет выглядеть примерно так:

class TimerComponent extends PureComponent {
      static propTypes = {
           onInterval: PropTypes.func.isRequired,
           interval: PropTypes.number.isRequired,
           immediate: PropTypes.bool,
      }

      static defaultProps = {
            immediate: true,
      }

      componentDidMount() {
          this._intervalRef = setInterval(this.props.onInterval, this.props.interval);
          if (this.props.immediate) {
               this.props.onInterval();
          }
      }

      componentWillUnmount() {
          clearInterval(this._intervalRef);
      }

      render() {
          return null;
      }
}

// We will still use the composable function, but in a differnt way.
// The function stays the same
const changeAdvertComposer = function changeAdvertComposer(component) {
     // we start at -1 so the first call asks for 0
     let index = -1;

     // return the function that setInterval will call
    return function changeAdvert() {
          const adverts = component.props.adverts;
          if (!adverts || !adverts.length) {
               // we have no advertisements so lets exit
               return;
          }
          index++;
          // reset our index if needed
          if (index >= adverts.length) {
              index = 0;
          }
          // increment and grab our object
          const advert = adverts[index];

          // grab our state or a default failover structure
          const state = component.state.advert || { bg: '', text: '' };
          // set our state
          component.setState({
              advert: {
                   bg: advert.photo || state.bg,
                   text: advert.text || state.text,
              }
          });
     }
};

export ExampleAdvertManager extends Component {

    // setup some validations on our props
    static propTypes = {
         getAdverts: PropTypes.func.isRequired,
         adverts: PropTypes.arrayOf(PropTypes.shape({
              photo: PropTypes.string,
              text: PropTypes.string
         }))
    }

    constructor(props) {
         super(props);

         // we will store the state in the interval object so we can
         // check for null (simple way to find out if loading)
         this.state = {
              advert: null
         };
    }

    componentDidMount() {
         // we are loaded let's call our data action loader
         this.props.getAdverts();
    }

    render() {
        if (this.stage.advert) {
            return (
                <div>
                    <TimerComponent interval={4000} onInterval={changeAdvertComposer(this)} />
                    {
                       // render what you want to do from state
                    }
                </div>
            );
        } else if (this.props.adverts) {
            return (
                 <TimerComponent key="interval" interval={4000} onInterval={changeAdvertComposer(this)} />
            );
        }
        // we don't have data yet
        return null; // or some loading view
    }
}

Надеюсь, это поможет.

Пример кода

Запуск демоверсии

...