冒泡componentWillUpdate和componentDidUpdate

时间:2016-06-17 17:57:43

标签: javascript dom reactjs react-dom

我知道这不是惯用的React,但是我正在研究React组件(反向滚动视图),它需要在渲染之前和渲染之后获得下游虚拟DOM更改的通知。

这有点难以解释所以我创建了一个JSFiddle的起点:https://jsfiddle.net/7hwtxdap/2/

我的目标是让日志看起来像:

Some text
...
LineToAppendTo,otherrandomtext,...,endoflinetext,appendedString
...
End of file text

我知道React有时会批处理DOM更改并且那很好(即日志事件可能是Begin log render rendered small rendered small rendered small before update rendered big after update before update rendered big after update before update rendered big after update ) - 重要的一部分是我在DOM之前和之后实际收到通知变化。

我可以通过指定回调来手动近似行为,如下所示:https://jsfiddle.net/7hwtxdap/4/。然而,这种方法并不具有规模 - 例如,我需要让后代而不是儿童发生事件;而且不必将这些事件处理程序添加到我使用的每个组件中。

用例:

我正在制作一个"反向滚动组件"对于消息(其中,当添加新元素或现有元素改变大小时,滚动更改的基础是从内容的底部而不是每个消息传递应用程序的顶部ala)动态修改子项(类似于{{1} } class但具有可变高度,来自ajax的数据等)。这些不是从类似Flux的状态存储中获取数据,而是使用pub-sub数据同步模型(与setTimeout()非常接近)。

要进行反向滚动工作,您需要执行以下操作:

before update; rendered big; rendered big; after update; ...

重要的是,对于这种设计,我希望能够将元素包裹在其他不是“反向滚动”的元素周围。 (即,不必使用特殊代码通知反向滚动处理程序它们已更改)。

4 个答案:

答案 0 :(得分:3)

根据设计,React认为对组件的setState调用应该触发that组件的重新呈现。我所知道的最可靠的方法就是组件的生命周期。

如何将notifier()回调作为道具传递,然后在子组件的componentWillMountcomponentDidUpdate生命周期挂钩中调用此函数?

Here's a working js-fiddle有这个想法。让我知道会发生什么。

修改1

如果您不希望在每个子组件中来回传递回调,那么我说您只是遇到Redux和{{{}的特定用例1}}。

使用Redux,组件可以通过reducer功能更改商店的状态。然后重新呈现在该商店中观察变化的每个组件。 Flux使用相同的想法。

所以我建议您使用redux来处理状态设置。要开始使用,您可以通过redux的创建者Dan Abramov完成these tutorial videos

答案 1 :(得分:1)

如果不知道为什么需要此功能,很难知道问题的最佳解决方案是什么。但是,我将解决这一具体问题:

  例如,我需要从后代而不是孩子那里冒泡;而且不必将这些事件处理程序添加到我使用的每个组件中。

我建议您对此解决方案进行两处更改。第一个是考虑使用context,但请务必在决定之前阅读警告。这将允许您在单个组件中定义回调,并在所有后代中立即使用它们。

第二个更改是将所有日志记录逻辑移动到单独的组件中,并让其他组件根据需要继承它。该组件看起来像:

#define x y
#define a b\
c\\
#define z

这样,您可以轻松地将此功能添加到新组件中,而无需重复逻辑。我已使用工作演示版更新了您的jsfiddle

答案 2 :(得分:1)

正如James所说,您可以使用上下文来定义一个回调函数,您可以将其传递给所有后代组件,如果您不想扩展组件,可以使用装饰器将高阶组件包装在它们周围。像这样:

在你的容器中:

class Container extends React.Component {
    static childContextTypes =  {
        log: React.PropTypes.func.isRequired
    };

   getChildContext() {
      return {
            log: (msg) => {
                console.log(msg)
            }
        }
    }

  render() {
    console.log("Begin Log")
    return (
        <div>
          <MyElement />
          <MyElement />
          <MyElement />
        </div>
    );
  }
}

装饰者类:

export const Logger = ComposedComponent => class extends React.Component {
    static contextTypes = {
        log: React.PropTypes.func.isRequired
    };

    constructor(props) {
        super(props)
    }

    componentWillReceiveProps(nextProps) {
        this.context.log('will receive props');
    }

    componentWillUpdate() {
        this.context.log('before update');
    }

    componentDidUpdate() {
        this.context.log('after update');
    }

  render() {
      return <ComposedComponent {...this.props}/>
  }
} 

然后装饰你想要的组件

@Logger
class MyElement extends React.Component {
...
}

答案 3 :(得分:0)

也许您可以查看react-virtualized,其中有反向滚动列表的例子。

export default class Example extends Component {
  constructor (props) {
    super(props)

    this.state = {
      list: []
    }
  }

  componentDidMount () {
    this._interval = setInterval(::this._updateFeed, 500)
  }

  componentWillUnmount () {
    clearInterval(this._interval)
  }

  render () {
    const { list } = this.state

    return (
      <div className={styles.VirtualScrollExample}>
        <VirtualScroll
          ref='VirtualScroll'
          className={styles.VirtualScroll}
          width={300}
          height={200}
          rowHeight={60}
          rowCount={list.length}
          rowRenderer={::this._rowRenderer}
        />
      </div>
    )
  }

  _updateFeed () {
    const list = [ ...this.state.list ]

    list.unshift(
      // Add new item here
    )

    this.setState({ list })

    // If you want to scroll to the top you can do it like this
    this.refs.VirtualScroll.scrollToRow(0)
  }

  _rowRenderer (index) {
    return (
      // Your markup goes here
    )
  }
}