React事件监听器和重新安装

时间:2017-01-02 03:28:54

标签: javascript reactjs components

你好我上周一直在学习React,我解决了一个问题,但我想知道为什么它解决了我的问题。

以下是代码示例:

export default class ExampleComponent extends Component{

    constructor(...arg){
        super(...arg);
        this.goToNextPage = this.goToNextPage.bind(this);
        this.state = { currentPage: 1, 
                       chapterImages: []};
    }

    goToNextPage(){
        this.setState({
          ...this.state,
          currentPage: R.inc(this.state.currentPage)
        });
    }

    render(){
        return (<button onClick={this.goToNextPage}>next</button>)
    }
}

我有一些看起来像这样的东西。在我的真实代码中,我正在进行ajax调用,这就是我的组件卸载和重新安装的原因...最后,当我点击我的按钮时,我收到以下错误:

Warning: setState(...): Can only update a mounted or mounting component.
This usually means you called setState() on an unmounted component.
This is a no-op. 

但是如果我从构造函数中删除这一行:

this.goToNextPage = this.goToNextPage.bind(this);

并将render方法中的行修改为:

return (<button onClick={this.goToNextPage.bind(this)}>next</button>)

它完美无缺,但我不知道为什么会出现上述错误。

我去了那条路线,因为当我卸载我的组件时,它会在按钮点击监听器中保留绑定方法“goToNextPage”的引用。

如何通过在构造函数中保持绑定来解决该问题?

1 个答案:

答案 0 :(得分:0)

这是我真正的代码类:

export default class MangaChapter extends Component{

  constructor(...arg){
    super(...arg);
    this._getPageId = R.prop('pageId');
    this._getPages = R.prop('pages');
    this._generatePageMenuItem = (pageId) => (<MenuItem key={pageId} value={pageId} primaryText={`${pageId}`} />);
    this._pageToMenuItem = R.compose(this._generatePageMenuItem, this._getPageId);
    this._getMangaPageMenuItems = R.compose(R.map(this._pageToMenuItem), this._getPages);
    this._toImageNode = (chapterPage) => (<img src={chapterPage.url} style={{display: 'block', margin: '0 auto'}}/>)
    this._preloadImage = (chapterPage) => new Image().src = chapterPage.url;
    this._preloadImages = R.forEach(this._preloadImage);
    this._getImagePage = R.compose(R.map(this._toImageNode), this._preloadImages, this._getPages);
    this.state = { currentPage: 1, chapterImages: this._getImagePage(this.props.chapter) };
    this.goToPreviousPage = this.goToPreviousPage.bind(this);
    this.goToNextPage = this.goToNextPage.bind(this);
  }
  componentWillMount(){
    this.props.getChapter(this.props.params.mangaId, this.props.params.chapterNum);
  }

  componentWillUnmount(){
    console.log('UNMOUNT');
  }

  componentWillReceiveProps(nextProps){
    const { chapter } = nextProps;
    this.state = { currentPage: 1, chapterImages: this._getImagePage(chapter) };
  }

  handleChange(event, index, currentPage){
    this.setState({currentPage});
  };

  goToPreviousPage(){
    this.setState({
      currentPage: R.dec(this.state.currentPage)
    });
  }

  goToNextPage(){
    this.setState({
      currentPage: R.inc(this.state.currentPage)
    });
  }

  displayPage(){
    return this.state.chapterImages[R.dec(this.state.currentPage)]
  }

  componentDidMount(){

  }

  render(){
    const { chapter, loading } = this.props;
    const chapterName = chapter.name;
    const chapterNum = this.props.params.chapterNum;

    if(loading) return (<CircularProgress size={80} thickness={5} style={loadingStyle} />);

    return (
      <div>
        <h2 style={{textAlign: 'center'}}>Chapter { chapterNum } : { chapterName } </h2>
        <div>
          {this.displayPage()}
        </div>
        <div>
        <br/>
          <Row  style={{textAlign: 'center'}}>
            <Col sm={4}>
              <RaisedButton
                label="Previous"
                primary={true}
                icon={<ChevronLeft />}
                onClick={this.goToPreviousPage}
              />
            </Col>
            <Col sm={4}>
              Page
              <SelectField
                value={""}
                onChange={this.handleChange.bind(this)}
                style={{width: '80px', display: 'inline-block'}}
                value={this.state.currentPage}
              >
                {this._getMangaPageMenuItems(chapter)}
              </SelectField>
              of { chapter.pages.length }
            </Col >
            <Col sm={4}>
              <RaisedButton
                labelPosition="before"
                label="Next"
                primary={true}
                icon={<ChevronRight />}
                style = {{display: 'inline-block'}}
                onClick={this.goToNextPage}
              />
            </Col>
          </Row>
        </div>
      </div>
    );
  }
}

问题在于构造函数中的这两个方法:

    this.goToPreviousPage = this.goToPreviousPage.bind(this);
    this.goToNextPage = this.goToNextPage.bind(this);

如果我这样做,我会得到如上所述的错误,如果我把这些方法放在componentDidMount方法中那样:

  componentDidMount(){
    this.goToPreviousPage = this.goToPreviousPage.bind(this);
    this.goToNextPage = this.goToNextPage.bind(this);
  }

它完美无缺,你知道为什么吗?