重新呈现列表的单行而不重新呈现整个列表
Re-rendering a single row of a list without re-rendering the entire list
我们正在尝试实现一个与 the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
一样工作的联系人列表
具体来说,我们试图在行悬停时显示一个复选框而不是头像。
我们只想捕获并重新渲染感兴趣的行(悬停时)并相应地显示 avatar/checkbox ...这似乎是一项简单的任务,但我们无法将渲染隔离到悬停的行(而不是重新渲染整个列表)
你对如何做这样的事情有什么建议吗?
我们的临时解决方案使用一个容器组件来处理 table:
当悬停一行时,我们从 Table
组件的 onRowHover
中捕获它并将其保存在容器状态中。这会触发整个列表的重新渲染,性能非常差。
您可以观看有关该问题的视频 here。
这是一个代码示例:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
提前致谢。
您可以将您的行包装到一个实现 shouldComponentUpdate
的新组件中,如下所示:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
然后你可以像这样在你的 ContactList
组件中使用它:
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
如果您不想手动实现 shouldComponentUpdate
,您可以使用 React 的 PureRenderMixin or check a lib like recompose,它提供了有用的帮助程序,例如 pure
。
编辑
正如 OP 和@Denis 所指出的,上述方法不能很好地处理 Table
组件的某些功能。具体来说,TableBody
对其子项的子项进行了一些操作。更好的方法是像这样定义 ContactRow
组件:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
然后像这样使用它
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
但我想 TableRow
仅在必要时重新渲染是每个人都会从中受益的功能,因此可能需要 PR :)
我们正在尝试实现一个与 the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
一样工作的联系人列表
具体来说,我们试图在行悬停时显示一个复选框而不是头像。
我们只想捕获并重新渲染感兴趣的行(悬停时)并相应地显示 avatar/checkbox ...这似乎是一项简单的任务,但我们无法将渲染隔离到悬停的行(而不是重新渲染整个列表)
你对如何做这样的事情有什么建议吗?
我们的临时解决方案使用一个容器组件来处理 table:
当悬停一行时,我们从 Table
组件的 onRowHover
中捕获它并将其保存在容器状态中。这会触发整个列表的重新渲染,性能非常差。
您可以观看有关该问题的视频 here。
这是一个代码示例:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
提前致谢。
您可以将您的行包装到一个实现 shouldComponentUpdate
的新组件中,如下所示:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
然后你可以像这样在你的 ContactList
组件中使用它:
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
如果您不想手动实现 shouldComponentUpdate
,您可以使用 React 的 PureRenderMixin or check a lib like recompose,它提供了有用的帮助程序,例如 pure
。
编辑
正如 OP 和@Denis 所指出的,上述方法不能很好地处理 Table
组件的某些功能。具体来说,TableBody
对其子项的子项进行了一些操作。更好的方法是像这样定义 ContactRow
组件:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
然后像这样使用它
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
但我想 TableRow
仅在必要时重新渲染是每个人都会从中受益的功能,因此可能需要 PR :)