简体   繁体   中英

page scroll_to #id after component re-rendered in React

I have a component JumpList, which is a side panel with links of two categorys (A an B), which each have subcategories (A1, A2, A3, and B1, B2, B3). My component ReactPage combines the JumpList with the content for the page, which can either be ContentPageA or ContentPageB, which correspond to the major categories. Each of these pages has headers which correspond to the subcategories (ex. A1, A2, and A3).

If the state of ReactPage is {a: true} the ContentPageA is rendered and clicking any of the A1-A3 links causes the page to scroll down to where those headers are on the page. Clicking any of B1-B3 changes the state of ReactPage to {a: false} and ContentPageB is rendered. What I'd like is for to also do the same scroll effect that clicking the same page links do, but the IDs used to reference these spots on the page do not exist at the time the code is run because React is not done rendering the page after the state change.

Is there a way I can make it so that this process happens in a React-y way? I could just hide the unused divs, and use toggling classes upon click, but that doesn't feel very Reacty (the truth of the DOM would change without a re-rendering)

some example code:

var ReactPage = React.createClass({
  getInitialState: function() {
    return {
      a: true
    };
  },

  handlePageChange(id, a){
    if ( a ){
      if (this.state.a === false){
        this.setState({a: true});
      }
    }else {
      if (this.state.b === true){
        this.setState({a: false});
      }
    }

    $('html, body').animate({
      scrollTop: $("#" + id).offset().top
    }, 1000);
  }

  render: function(){
    var content;
    if (this.state.a) {
      content = <ContentPageA></ContentPageA>
    }else{
      content = <ContentPageB></ContentPageB>
    }

    return (
      <div>
        <SideJumpPanel pageChangeCallback={this.handlePageChange}></SideJumpPanel>
        {content}
      </div>
    )
  }
});


var SideJumpPanel = React.createClass({
  propTypes: {
    pageChangeCallback: React.PropTypes.func
  },

  scrollTo: function(id){
    return function() {
      this.pageChangeCallback(id);
    }
  }

  render: function() {
    return (
      <div>
        Jump to:
        A:
        <ul>
          <li onClick={this.scrollTo('A1', true)}>A1</li>
          <li onClick={this.scrollTo('A2', true)}>A2</li>
          <li onClick={this.scrollTo('A3', true)}>A3</li>
        </ul>
        B:
        <ul>
          <li onClick={this.scrollTo('B1', false)}>B1</li>
          <li onClick={this.scrollTo('B2', false)}>B2</li>
          <li onClick={this.scrollTo('B3', false)}>B3</li>
        </ul>
      </div>
    )
  }
});

(I may have left something out of the sample code, it's mainly just to illustrate)

Just found a solution, but still curious if there is a more zen way to do this. One solution as suggested in the second answer to this question is to use window.requestAnimationframe

React "after render" code?

You could just wrap your two pages in differents <div> an apply display: none; to the page you want to hide and display: block (or whatever display you want) to the page you want to show. ex:

<div id="A" style="display: block;">
  ... Your page A
</div>
<div id="B" style="display:none">
  ... Your page B
</div>

Then, in your js:

if(a){
  document.getElementById("A").style.display = "block";
  document.getElementById("B").style.display = "none";
} else {
  document.getElementById("B").style.display = "block";
  document.getElementById("A").style.display = "none";
}

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