React 生命周期方法顺序

React lifecycle methods order

我的项目有无限的幻灯片放映,我不确定我的方法是否顺序正确。我在 componentWillMount() 上调用 fetch 函数,然后在 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 link:https://stackblitz.com/edit/react-sshop

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

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