当我需要传递参数时,JSX Props 中没有 .bind() 或箭头函数对我来说毫无意义
No .bind() or Arrow Functions in JSX Props making no sense to me when I need to pass arguments
查看 eslint 规则,No .bind() or Arrow Functions in JSX Props。
它说不要使用箭头函数或绑定:
<div onClick={this._handleClick.bind(this)}></div>
<div onClick={() => console.log('Hello!'))}></div>
而是使用:
<div onClick={this._handleClick}></div>
一切都很好,但是如何在点击事件中将参数传递给此函数?
这是我天真的代码示例:
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive() {
console.log(arguments);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={this.handleToggleActive}>Toggle Active</Button>
</li>
))}
</ul>
);
}
}
如何让 user.id
进入我的 handleToggleActive
函数?当我尝试类似的东西时:
<Button onClick={this.handleToggleActive(user.id)}>Toggle Active</Button>
渲染时执行,我能看到的唯一其他方法是使用箭头函数?
正确的做法是什么?
我猜你可以使用 data-*
属性。
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive(event) {
console.log(event.target.dataset.id);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={this.handleToggleActive} data-id={user.id}>Toggle Active</Button>
</li>
))}
</ul>
);
}
}
使用绑定和箭头函数,将创建一个新函数,将一个全新的属性传递给您的组件,导致不必要的重新渲染和垃圾收集器的额外工作,使用 data-attributes 是一个糟糕的设计并且不是React 做事的方式,你不应该直接访问 DOM,而是让 React 处理事情。取而代之的是,您可以只创建一个新组件并将处理程序作为道具传递,在组件的方法中处理 id 的 "binding":
class User extends Component {
handleClick() {
this.props.handleToggleActive(this.props.user.id);
}
render() {
return (
<li>
<Link to={`/users/${this.props.user.id}`}>{this.props.user.name}</Link>
<Button onClick={this.handleClick}>Toggle Active</Button>
</li>
)
}
}
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive(userId) {
console.log(userId);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<User key={user.id} user={user} handleToggleActive={this.handleToggleActive} />
))}
</ul>
);
}
}
为了解决性能问题(这就是规则存在的原因),使用记忆化怎么样?
例如,使用包 lru-memoize
你会得到(带有一个哑组件)像这样的东西:
var memoize100 = require('lru-memoize')(100);
let handleToggleActive = (user) => () => {
console.log(user.id)
}
handleToggleActive = memoize100(handleToggleActive)
const UserList = (props) =>
<ul className="user-list">
{ props.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={handleToggleActive(user)}>Toggle Active</Button>
</li>
))
}
</ul>
使用箭头函数或绑定将在每次渲染组件时创建不同的回调实例。因此,每次都会重新创建该函数,而旧的函数会被垃圾收集,从而使垃圾收集器过载。此外,由于这个原因,纯组件和在 shouldcomponentupdate 中进行浅比较的组件将始终重新渲染,因为它会发现其 props 发生变化,因为旧函数被丢弃,每次渲染父组件时都会创建一个新函数。
我们可以将 属性 初始化器语法与柯里化结合使用,以避免在 JSX 代码中使用绑定或箭头函数。
handleClick = (param) => (e) => {
console.log('Event', e);
console.log('Parameter', param);
}
render() {
<button onClick={this.handleClick('Parameter')}></button>
}
查看 eslint 规则,No .bind() or Arrow Functions in JSX Props。
它说不要使用箭头函数或绑定:
<div onClick={this._handleClick.bind(this)}></div>
<div onClick={() => console.log('Hello!'))}></div>
而是使用:
<div onClick={this._handleClick}></div>
一切都很好,但是如何在点击事件中将参数传递给此函数?
这是我天真的代码示例:
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive() {
console.log(arguments);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={this.handleToggleActive}>Toggle Active</Button>
</li>
))}
</ul>
);
}
}
如何让 user.id
进入我的 handleToggleActive
函数?当我尝试类似的东西时:
<Button onClick={this.handleToggleActive(user.id)}>Toggle Active</Button>
渲染时执行,我能看到的唯一其他方法是使用箭头函数?
正确的做法是什么?
我猜你可以使用 data-*
属性。
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive(event) {
console.log(event.target.dataset.id);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={this.handleToggleActive} data-id={user.id}>Toggle Active</Button>
</li>
))}
</ul>
);
}
}
使用绑定和箭头函数,将创建一个新函数,将一个全新的属性传递给您的组件,导致不必要的重新渲染和垃圾收集器的额外工作,使用 data-attributes 是一个糟糕的设计并且不是React 做事的方式,你不应该直接访问 DOM,而是让 React 处理事情。取而代之的是,您可以只创建一个新组件并将处理程序作为道具传递,在组件的方法中处理 id 的 "binding":
class User extends Component {
handleClick() {
this.props.handleToggleActive(this.props.user.id);
}
render() {
return (
<li>
<Link to={`/users/${this.props.user.id}`}>{this.props.user.name}</Link>
<Button onClick={this.handleClick}>Toggle Active</Button>
</li>
)
}
}
export class UserList extends Component {
constructor(props) {
super(props);
this.handleToggleActive = this.handleToggleActive.bind(this);
}
handleToggleActive(userId) {
console.log(userId);
}
render() {
return (
<ul className="user-list">
{this
.props
.users
.map(user => (
<User key={user.id} user={user} handleToggleActive={this.handleToggleActive} />
))}
</ul>
);
}
}
为了解决性能问题(这就是规则存在的原因),使用记忆化怎么样?
例如,使用包 lru-memoize
你会得到(带有一个哑组件)像这样的东西:
var memoize100 = require('lru-memoize')(100);
let handleToggleActive = (user) => () => {
console.log(user.id)
}
handleToggleActive = memoize100(handleToggleActive)
const UserList = (props) =>
<ul className="user-list">
{ props.users
.map(user => (
<li key={user.id}>
<Link to={`/users/${user.id}`}>{user.name}</Link>
<Button onClick={handleToggleActive(user)}>Toggle Active</Button>
</li>
))
}
</ul>
使用箭头函数或绑定将在每次渲染组件时创建不同的回调实例。因此,每次都会重新创建该函数,而旧的函数会被垃圾收集,从而使垃圾收集器过载。此外,由于这个原因,纯组件和在 shouldcomponentupdate 中进行浅比较的组件将始终重新渲染,因为它会发现其 props 发生变化,因为旧函数被丢弃,每次渲染父组件时都会创建一个新函数。
我们可以将 属性 初始化器语法与柯里化结合使用,以避免在 JSX 代码中使用绑定或箭头函数。
handleClick = (param) => (e) => {
console.log('Event', e);
console.log('Parameter', param);
}
render() {
<button onClick={this.handleClick('Parameter')}></button>
}