将焦点设置在reactjs中显示的数组的最后一个元素上

时间:2016-06-23 16:58:59

标签: javascript arrays reactjs focus

我正在使用reactjs编写聊天应用程序,我试图找出每次向阵列添加新消息时如何将焦点更改为最新消息。 我的反应聊天窗口如下所示:

<ChatHeader />
<Messages datas={this.state.datas} />
<ChatFooter sendMessage={this.sendMessage} />

我的Messages组件如下所示:

var Messages = React.createClass({
  render: function() {
    // Loop through the list of messages and create array of Row components
    var Rows = this.props.datas.map(function(data) {
      return (
        <Row username={data.username} message={data.message} /> 
      )
    });

    return (
        {Rows}
    );
  }
});

所以基本上每次我的主聊天窗口都会重新呈现时,我希望显示消息,列表一直向下滚动(显示最新消息 - 或者Rows数组中的最后几个内容)。我发现了一些关于转移焦点的问题,如this one here,但当我尝试将焦点转移到消息时,它将我带到了列表的顶部。理想情况下,我想添加这样的内容:

   var Rows = this.props.datas.map(function(data) {
      return (
        <Row username={data.username} message={data.message} /> 
      )
    });

//    Rows[Rows.length - 1].ref = "last";

所以我可以专注于数组中的最后一个元素但是我找不到像这样设置引用的方法。我见过的所有方法都显示了在标签内设置引用。是否存在分配这样的引用的方法?

我还想过为每一行提供一个唯一的ID号,并为每个新消息递增该数字,跟踪最大数字,然后使用它来引用最后一个元素,但似乎必须有更好的方法。实现这一目标的最佳方法是什么?

1 个答案:

答案 0 :(得分:0)

我们必须在React的书中使用被认为是反模式的内容并使用他们通常不赞成的ReactDOM.findDOMNode

基本上,我们要做的是在名为onRowRender Messages 类上创建一个方法,并将其绑定到 Messages this >构造函数。我们将使用map的可选第二个参数来跟踪已生成的行数,如果它是最后一行,我们将通过onRowRendered方法使用道具组件,我们会调用onRender

在每个组件的componentDidMount中,我们将检查this.props.onRender是否存在,如果存在,那么这意味着它是最后一行,因此我们调用{ {1}}并传递this.props.onRender,这是有问题的组件。

由于我们在组件安装后执行所有这些操作意味着我们将能够使用this来获取该呈现组件的原始javascript DOM元素。我们在父 Messages 类的处理程序中执行此操作。

完成所有这些后,我们得到了有问题的 DOMNode 的顶部偏移量,我们也得到它ReactDOM.findDOMNode然后设置{的顶部偏移量parentNode {1}}等于顶部偏移量。

注意:我在我的示例中使用了es6类语法,但它也适用于React.createClass。

parentNode

强制性的jsfiddle:https://jsfiddle.net/69z2wepo/46701/

或者,您可以通过将行的class Messages extends React.Component { constructor() { super(); this.onRowRender = this.onRowRender.bind(this); } onRowRender(row) { var rowDOM = ReactDOM.findDOMNode(row); var offsets = rowDOM.getClientRects()[0]; var parent = rowDOM.parentNode; parent.scrollTop = offsets.top; } render() { return ( <div className='messages'> {this.props.datas.map((data, i) => { return ( <Row key={data.key} username={data.username} message={data.message} onRender={i === this.props.datas.length - 1 ? this.onRowRender : null} /> ); })} </div> ); } } class Row extends React.Component { componentDidMount() { if (this.props.onRender) this.props.onRender(this); } render() { return ( <div> {this.props.message}&nbsp;&nbsp;&nbsp;by: {this.props.username} </div> ); } } var datas = [ { key: 1, username: 'test user #1', message: 'test message #1', }, { key: 2, username: 'test user #2', message: 'test message #2', }, { key: 3, username: 'test user #3', message: 'test message #3', }, { key: 4, username: 'test user #4', message: 'test message #4', }, { key: 5, username: 'test user #5', message: 'test message #5', }, ]; ReactDOM.render( <Messages datas={datas} />, document.getElementById('root') ); 道具更改为onRender,然后将onRowRender更改为:将父卷轴的滚动顶部设置为自己的卷轴高度:

onRender={this.onRowRender}

每次安装新行时,它都会将其滚动到底部。但是,通过我最初演示它的方式来确保如果最后大于消息组件的总高度,它将只滚动到的顶部,而不是整个列表的底部。 (我希望这是有道理的)