当一个组件的状态发生变化时,如何防止整个列表重新呈现?

时间:2018-04-05 02:21:59

标签: javascript reactjs bootstrap-4

我有一个列表组件,它可以获取数据并将结果映射到类似卡片的子组件上,以创建“卡片”列表。以下是一个例子:

class Card extends Component {
  constructor(props) {
    super(props);
    this.state = {
      activeTab: 1
    };
  }

  setActiveTab = (activeTab) => {
    this.setState({ activeTab });
  }

  render() {
    const activeTab = this.state.activeTab;

    return (
      <div className="card">
        <ul className="nav nav-tabs card-header-tabs">
          <li 
            className="nav-item" 
            key={ 1 }
            onClick={ () => this.setActiveTab(1) }
          >
            <a className="nav-link" href="#">Overview</a>
          </li>
          <li 
            className="nav-item" 
            key={ 2 }
            onClick={ () => this.setActiveTab(2) }
          >
            <a className="nav-link" href="#">Data</a>
          </li>
          <li 
            className="nav-item" 
            key={ 3 }
            onClick={ () => this.setActiveTab(3) }
          >
            <a className="nav-link" href="#">Edit</a>
          </li>
        </ul>
        <h3 className="card-title">{ this.props.title }</h3>
        { activeTab === 1 && 
          <h5 className="card-text">
            { this.props.body }
          </h5>    
        }
        { activeTab === 2 && 
          <div className="card-data">
            <a>Yes: { this.props.yes }</a>
            <a>No: { this.props.no }</a>
          </div>
        }
        { activeTab === 3 &&
          <div>
            <a 
              href="/cards"
              onClick={ this.props.onDelete }
              className="delete-card"
            >
              Delete
            </a>
          </div>
        }
      </div>
    )
  }
}
export default Card;


class CardList extends Component {
  componentDidMount() {
    this.props.fetchCards();
  }

  renderCards() {
    return this.props.cards.reverse().map(({ _id, title, body, yes, no }) => {
      return (
        <Card
          key={_id}
          title={title}
          body={body}
          yes={yes}
          no={no}
          onDelete={() => this.props.deleteSurvey(_id)}
        />
      );
    });
  }

  render() {
    return (
      <div className="container">
        <div className="row">{this.renderCards()}</div>
      </div>
    );
  }
}

每张卡都有标签,单击标签可确定每张特定卡片中显示的文字。选项卡/切换功能正常,但我的问题是单击选项卡时整个列表会重新呈现。因此,如果您位于列表的底部,则重新渲染会将您带回到列表的顶部。

我尝试过实现componentShouldUpdate,但到目前为止还没有成功。我想要卡片切换它的内容,列表仍然存在。使用shouldUpdate生命周期方法是否正确?还是有更好的方法?

3 个答案:

答案 0 :(得分:2)

我认为这是prbolem

 onClick={ () => this.setActiveTab(2) }

基本上,React决定在组件状态或道具中检测到change时重新渲染组件,并且它只对对象进行浅层比较。

在使用闭包时,每次调用Card组件渲染函数时都会动态地重新创建这些函数。 React检测传递给li元素的新对象(在JS中,函数被视为对象),然后重绘它。

要解决此问题,您可以执行以下操作:

<li className="nav-item" data-tab={ 1 }
                            onClick={this.navLinkClickHandler}>
                            <a className="nav-link" href="#">Overview</a>
                        </li>

然后在您的组件中定义navLinkClickHandler

navLinkClickHandler = (evt) => this.setState({activeTab: evt.target.dataset.tab})

有关React reconcilation的更多信息,请点击此处:

https://reactjs.org/docs/reconciliation.html

关于在渲染函数中使用闭包:

https://reda-bx.gitbooks.io/react-bits/content/conventions/08.closures-in-render.html

答案 1 :(得分:2)

<a> 标记中,您有 href =&#34;#&#34; `这将始终将您重定向到新的链接。 (#将重定向到当前页面重新渲染所有内容)。

  

最简单的解决方案删除href =&#34;#&#34; ,这会删除您的cursor: pointer;样式,但您可以重新添加回到你的   nav-item classname。

另一个简单的解决方案是您可以将 onClick 移动到标记并添加 evt.preventDefault()(这会阻止事件处理程序从执行默认操作,将页面加载到href)然而,这将使您必须单击锚标记,以便在<li>和{{之间填充1}}这可能不是你最好的解决方案。

<a>

然而,Thai Duong Tran提出了一个很好的观点,你不想每次都重新创建这个功能。

  

最佳解决方案是将您的匿名函数移动到类函数中。 (此解决方案还会删除href =&#34;#&#34;因此如果您需要该样式,则需要将<li className="nav-item" key={ 3 } > <a className="nav-link" href="#" onClick={ (e) => { e.preventDefault(); this.setActiveTab(3); } } > Edit </a> </li> 添加到您的CSS中

cursor: pointer;

你的删除操作也有同样的问题,你添加了一个href =&#34; / card&#34;所以你要小心。

如果您希望将路线操作移至不同的网页/网址。您应该考虑添加 <li className="nav-item" data-tab={ 1 } onClick={this.onNavClick} > <a className="nav-link">Overview</a> </li> onNavClick = (evt) => { this.setState({activeTab: evt.target.dataset.tab}); }

答案 2 :(得分:0)

在CARD组件中尝试此代码(如果您尚未尝试过)。可能工作正常。

shouldComponentUpdate(nextProps,nextState){
   if(this.state.activeTab  === nextState.activeTab ){
      return false
   } else {
      return true
   }
}