【问题标题】:ref is null - Semantic UI React Table rowref 为 null - 语义 UI 反应表行
【发布时间】:2018-01-16 22:33:42
【问题描述】:

我正在尝试将我的Table.rowref 传递给点击处理程序。也许我想通过更改类名来选择一行并在选择时突出显示它。但是它总是记录 null。

尝试:

<Table.Body>
    {_.map(data, ({ Name, Version, Type, Modified }) => (
        <Table.Row
          ref={(ref) => { this.row = ref; }}
          className=""
          key={Version}
          onClick={() => this.handleClick(this.row, Name, Version, Type, Modified)}
        >
            <Table.Cell>{Name}</Table.Cell>
            <Table.Cell>{Version}</Table.Cell>
            <Table.Cell>{Type}</Table.Cell>
            <Table.Cell>{Modified}</Table.Cell>
        </Table.Row>
    ))}
</Table.Body>

handleClick(row, Name, Version, Type, Modified) {
    const me = this;
    log.info('logging row', row); //null
    log.info('logging row data', Name, Version, Type, Modified); //works
}

有人知道为什么ref 在这里不能正常工作吗?

【问题讨论】:

  • 它应该可以工作,尽管您做错的是在地图的每次迭代中重新分配 this.ref。因此,最后一个Table.Row 应该被this.ref 引用,其他行应该没有引用。也许错误是相关的?
  • 顺便说一句,你正在覆盖 ref
  • 另一件事,我在他们的docs 中看不到对refs 的任何支持。您可能会发现 this pattern 很有帮助。
  • 我有一个更好和更react'ish 的解决方案给你。我会把它写成答案
  • 那太棒了 - 仍然卡在这里

标签: javascript reactjs semantic-ui semantic-ui-react


【解决方案1】:

正如我在 cmets 中提到的,我在他们的 docs 中看不到对 refs 的任何支持。
至于您的 ref 实现,您将在每次迭代中覆盖 this.row

在我看来,无论如何,对此有一个更反应的解决方案。

如果您想跟踪选择了哪些行,您将需要一个处于您状态的对象,该对象将在每次单击行时更新,一种查找表对象。

要创建这样的查找表,您需要为每一行提供 id(您可以使用数据对象中的 id 或数组的索引)。

为了能够将id 传递给Table.Row 并作为onClick 的参数备份到父级,您可以将Table.Row 包装为class 组件,该组件将处理点击和它返回的数据。

例如MyRow 将期望获得rowIdonClick 事件,当然还有我们将传递给Table.Rowactive 道具。

MyRow 被点击时,它会将this.props.rowId 作为参数传递给父级,父级将更新查找表。

以下是此用例的运行示例:

const { Table } = semanticUIReact; // --> import { Table } from 'semantic-ui-react'

const usersFromServer = [
  { name: 'john', age: 25, gender: 'male', id: 1 },
  { name: 'jane', age: 22, gender: 'female', id: 2 },
  { name: 'david', age: 31, gender: 'male', id: 3 },
  { name: 'jain', age: 29, gender: 'female', id: 4 }
];


class MyRow extends React.Component {
  onClick = (e) => {
    const { onClick, rowId } = this.props;
    onClick(rowId, e);
  }

  render() {
    const { data, active } = this.props;
    return (
      <Table.Row onClick={this.onClick} active={active}>
        <Table.Cell>{data.name}</Table.Cell>
        <Table.Cell>{data.age}</Table.Cell>
        <Table.Cell >{data.gender}</Table.Cell>
      </Table.Row>
    );
  }
}


class TableExample extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeRows: []
    }
  }

  onRowClick = (id, e) => {
    const { activeRows } = this.state;
    const nextRows = {
      ...activeRows,
      [id]: !activeRows[id]
    }
    this.setState({ activeRows: nextRows });
  }

  render() {
    const { activeRows } = this.state;
    return (
      <Table unstackable>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>Name</Table.HeaderCell>
            <Table.HeaderCell>Age</Table.HeaderCell>
            <Table.HeaderCell>Gender</Table.HeaderCell>
          </Table.Row>
        </Table.Header>

        <Table.Body>
          {
            usersFromServer.map((u) => {
              const isActive = activeRows[u.id];
              return (
                <MyRow
                  active={isActive}
                  key={u.id}
                  rowId={u.id}
                  data={u}
                  onClick={this.onRowClick}
                />
              );
            })
          }

        </Table.Body>
      </Table>
    )
  }
}

ReactDOM.render(<TableExample />, document.getElementById('root'));
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.9/semantic.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-react@0.77.2/dist/umd/semantic-ui-react.min.js"></script>
<div id="root"></div>

【讨论】:

  • 非常感谢您的回答.. 还没有时间测试它,但我想出的解决方案要简单得多——看看您的想法
  • 这个挑战有很多解决方案,但最好的做法是创建另一个组件并使用“组件组合模式”。
  • @Sagivb.g 如果我们想滚动到给定的行怎么办?我希望能够接受裁判并做ref.current.scrollIntoView()
【解决方案2】:

不是一个非常 React-ish 的解决方案,但非常简单: 我没有使用refs,而是为每一行创建了一个唯一的 ID。然后在 clickHandler 中我可以识别该行并将其标记为选中:

export default class TableExampleSortable extends Component {

    constructor(props) {
        super(props);

        this.state = {
            column: null,
            data: props.convo,
            direction: null,
        };

        this.handleClick = this.handleClick.bind(this);
    }


    handleSort = clickedColumn => () => {
        const { column, data, direction } = this.state;

        if (column !== clickedColumn) {
            this.setState({
                column: clickedColumn,
                data: _.sortBy(data, [clickedColumn]),
                direction: 'ascending',
            });

            return;
        }

        this.setState({
            data: data.reverse(),
            direction: direction === 'ascending' ? 'descending' : 'ascending',
        });
    }

    handleClick(Version) {
        const element = document.getElementById(Version);
        if (element.className === 'selected') {
            element.className = 'notSelected';
        } else {
            element.className = 'selected';
        }
    }

    render() {
        const { column, data, direction } = this.state;

        return (
            <div>
                <Table sortable selectable>
                    <Table.Header>
                        <Table.Row>
                            <Table.HeaderCell sorted={column === 'Name' ? direction : null} onClick={this.handleSort('Name')}>
                                Name
                            </Table.HeaderCell>
                            <Table.HeaderCell sorted={column === 'Version' ? direction : null} onClick={this.handleSort('Version')}>
                                Version
                            </Table.HeaderCell>
                            <Table.HeaderCell sorted={column === 'Type' ? direction : null} onClick={this.handleSort('Type')}>
                                Type
                            </Table.HeaderCell>
                            <Table.HeaderCell sorted={column === 'Modified' ? direction : null} onClick={this.handleSort('Modified')}>
                                Modified
                            </Table.HeaderCell>
                        </Table.Row>
                    </Table.Header>
                    <Table.Body>
                        {_.map(data, ({ Name, Version, Type, Modified }) => (
                            <Table.Row
                              id={Version}
                              className="notSelected"
                              key={Version}
                              onClick={() => this.handleClick(Version)}
                            >
                                <Table.Cell>{Name}</Table.Cell>
                                <Table.Cell>{Version}</Table.Cell>
                                <Table.Cell>{Type}</Table.Cell>
                                <Table.Cell>{Modified}</Table.Cell>
                            </Table.Row>
                        ))}
                    </Table.Body>
                </Table>
            </div>
        );
    }
}

【讨论】:

  • 这种模式可能会影响性能。您正在每次迭代中创建一个新的函数引用,该引用将在每次渲染调用时运行。更不用说这不是一个可扩展或可测试的解决方案。简单并不总是更好:) 你应该阅读this answerthis lint rule 以了解模式的差异
  • 编辑了整个类——我的函数在渲染之外。看一看。它不是在每次渲染时都被调用
  • 似乎你从我提供的链接中选择了my answer(currying)。如果您仔细看,我在答案的底部写道:“请注意,这种方法不能解决在每次渲染时创建新实例的问题”。您仍在创建一个新函数,因为每次调用外部函数时都会返回一个新函数。如果不创建另一个 class 组件将创建一个实例并让 react 使用 props 和 diffing 算法实现它的魔力,就无法绕过这一点
  • 用 onClick 中的箭头功能抓住了我。谢谢你的回答!
猜你喜欢
  • 1970-01-01
  • 2019-12-26
  • 1970-01-01
  • 2021-01-27
  • 2021-09-20
  • 1970-01-01
  • 2018-12-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多