根据父组件的状态来突变子组件,而无需重新渲染

时间:2018-08-16 21:34:47

标签: javascript jquery reactjs dom lifecycle

目标

我正在尝试从父组件管理mouseenter和mouseleave事件,以获取不断重新呈现的子组件的集合。

我正在为清单的集合构建一个可重用的组件,它可以执行诸如分页和在清单悬停时的其他一些功能。

因此,要使其可重复使用,我必须维护父级CollectionComponent悬停的列表的状态,并根据父级的状态对每个单独的列表组件进行突变。


代码

这是我正在使用的组件(我将它们剥离为最基本的形式):

列表组件:

import React from 'react'
import $ from 'jquery'
import CollectionComponent from './CollectionComponent'
import Listing from './Listing'

export default class Listings extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      listings: this.props.listings,
    }
  }

  render() {

    return (<section className="listing-results">
      {this.state.listings.map( listing => 
        <CollectionComponent results={this.state.listings} IndividualResult={Listing} perPage={this.props.perPage} options={options}/> 
      )}
    </section>)
  }
}

集合组件:

import React from 'react'

export default class CollectionComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      results: this.props.results,
      hoveredId: null
    }
  }

  componentDidMount() {
    this.$listings = $('.result-card')
    $(this.$listings).mouseenter(this.toggleInfoIn).mouseleave(this.toggleInfoOut)
  }

  toggleInfoIn = e => {
    var { target } = e
    var infoId = $(target).data('id')
    this.setState({hoveredId: infoId})
  }

  toggleInfoOut = e => {
    this.setState({hoveredId: null})
  }

  render() {
    const {results, IndividualResult, perPage, options} = this.props

    return (
      <div className="paginated-results">
        {this.state.results.map( result =>  
          <IndividualResult key={result.id} result={result} options={options}/> 
        )}
      </div>
    )
  }
}

单个列表组件:

import React from 'react'

export default class Listing extends React.Component {
  constructor(props) {
    super(props)
  }

  render() {

    const { listing, hoveredId } = this.props

    return (
      <div className="result-card" data-id={listing.id}>
        <div className={hoveredId === listing.id ? 'hovered' : ''}>
          Listing Content
        </div>
      </div>
    )
  }
}

我知道我可以用更高阶的组件来构造CollectionComponent,使它更整洁,但是一旦我可以在基本设置下正常工作,我将把它留待以后重构。


问题

我的问题是,每次我悬停并更改父组件的状态时,它都会重新渲染子组件,因为它们的道具取决于父组件的状态。一旦发生这种情况,对我的jQuery清单集合的引用将不再有效。因此,鼠标事件会附加到不再存在的旧DOM元素上。


我该如何以不同的方式构造它,以便:

  1. 子元素的道具更新而无需重新渲染,或者
  2. jQuery集合引用不变

我真的想避免每次组件更新时都获得新的jQuery集合。

1 个答案:

答案 0 :(得分:2)

悬停行为应仅限于单独的列表组件,而不是集合组件。

由于Collections组件保持当前悬停的物品的状态,因此最好将处理程序作为prop的一部分传递,然后根据{{1}设置的状态变化再次呈现列表}组件。

在必要时使用基于反应的事件处理程序,这使它成为受控组件。将状态放在Collections中不是个好主意,反应可以为您处理状态。

列表

DOM

收藏集

import React from 'react'

export default class Listing extends React.Component {
  constructor(props) {
    super(props);

    this.onMouseEnter = this.onMouseEnter.bind(this);
    this.onMouseLeave = this.onMouseLeave.bind(this);
  }
  onMouseEnter() {
        this.props.onMouseEnter({ listingId: this.props.listing.id });
  }
  onMouseLeave() {
        this.props.onMouseLeave();
  }
  render() {

    const { listing, hoveredId } = this.props
    const listingId = listing.id;
    const isHovered = this.props.hoveredId === listing.id;

    return (
      <div className="result-card" onMouseEnter={this.onMouseEnter} onMouseLeave={onMouseLeave}>
        <div className={isHovered ? 'hovered' : ''}>
          Listing Content
        </div>
      </div>
    )
  }
}