反应生命周期方法的顺序

时间:2018-05-12 15:12:18

标签: javascript reactjs setinterval application-lifecycle

我的项目有无限幻灯片,我不确定我的方法是否正确。我在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 undefinedCannot read property 'text' of undefined

STACKBLITZ链接:https://stackblitz.com/edit/react-sshop

1 个答案:

答案 0 :(得分:1)

这是您可能希望使用当前代码执行此操作的示例。我不同意这种做法,但对于初学者来说,这应该可以帮助你开始探索更多的React-ish编码方式。

// 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成为可组合的......兔子洞更深了。

实际上我会把间隔计时器作为它自己的一个组件,它会呈现null并触发对我的组件的回调。只是让一切都更清洁。这看起来像这样:

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
    }
}

我希望这会有所帮助。

Code Example

Running Demo