React无状态组件中的事件处理程序

aSt*_*ign 67 javascript reactjs

试图找出在React无状态组件中创建事件处理程序的最佳方法.我可以这样做:

const myComponent = (props) => {
    const myHandler = (e) => props.dispatch(something());
    return (
        <button onClick={myHandler}>Click Me</button>
    );
}
Run Code Online (Sandbox Code Playgroud)

这里的缺点是,每次渲染此组件时,都会创建一个新的"myHandler"函数.有没有更好的方法在仍然可以访问组件属性的无状态组件中创建事件处理程序?

Jed*_*rds 55

将处理程序应用于函数组件中的元素通常应如下所示:

const f = props => <button onClick={props.onClick}></button>
Run Code Online (Sandbox Code Playgroud)

如果你需要做更复杂的事情,比如创建处理程序,绑定它们,或者类似的东西,它是a)组件不应该是无状态的标志,或者b)你应该在外部有状态容器中创建处理程序零件.

顺便说一句,并且稍微破坏我的第一点,除非组件处于应用程序的特别密集重新渲染的部分,否则无需担心创建箭头函数render().

  • 这如何避免每次渲染无状态组件时创建一个函数? (2认同)

rya*_*ent 33

使用新的React钩子功能,它看起来像这样:

const HelloWorld = ({ dispatch }) => {
  const handleClick = useCallback(() => {
    dispatch(something())
  })
  return <button onClick={handleClick} />
}
Run Code Online (Sandbox Code Playgroud)

useCallback 创建一个memoised函数,这意味着在每个渲染周期中不会重新生成新函数.

https://reactjs.org/docs/hooks-reference.html#usecallback

但是,这仍处于提案阶段.

  • React Hooks已在React 16.8中发布,现已成为React的正式组成部分。因此,此答案非常有效。 (7认同)
  • @herman根本没有区别(除了小的性能损失),这就是为什么我们在下面评论的这个答案有点可疑:)任何没有依赖项数组的钩子都会在每次更新后运行(已经讨论过)靠近 useEffect 文档的开头)。就像我提到的,如果您想要对您计划传递给密集/昂贵地重新渲染的子组件的回调函数进行稳定/记忆的引用,那么您几乎只想使用 useCallback ,并且引用相等很重要。任何其他用法,只需每次在渲染中创建一个新函数即可。 (3认同)
  • 只是要注意,作为eslint-plugin-react-hooks程序包的一部分,建议的详尽穷举规则说:“仅当使用一个参数调用时,React Hook useCallback不会执行任何操作。”因此,在这种情况下,应为空数组作为第二个参数传递。 (2认同)
  • 在上面的示例中,使用 `useCallback` 没有提高效率 - 并且您仍然在每次渲染时生成一个新的箭头函数(传递给 `useCallback` 的参数)。`useCallback` 仅当将回调传递给依赖引用相等性来防止不必要的渲染的优化子组件时才有用。如果您只是将回调应用于按钮等 HTML 元素,则不要使用“useCallback”。 (2认同)

Phi*_*yen 14

怎么样这样:

const myHandler = (e,props) => props.dispatch(something());

const myComponent = (props) => {
 return (
    <button onClick={(e) => myHandler(e,props)}>Click Me</button>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 好好想一想!遗憾的是,这并没有解决每次渲染调用创建新函数的问题:https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md (13认同)
  • 有一个父常规组件,它具有myHandler的实现,然后简单地将它传递给子组件 (4认同)

rya*_*ffy 6

如果处理程序依赖于更改的属性,则由于缺少缺少要在其上进行缓存的有状态实例,因此每次都必须创建处理程序。另一个可行的替代方法是根据输入的道具来记住处理程序。

几个实现选项 lodash._memoize R.memoize 快速存储


jas*_*ian 5

这是我用 React 和 Redux 在打字稿中编写的简单最喜欢的产品列表。您可以在自定义处理程序中传递您需要的所有参数,并返回一个EventHandler接受原始事件参数的新参数。它MouseEvent在这个例子中。

隔离的函数使 jsx 更干净,并防止破坏多个 linting 规则。比如jsx-no-bind, jsx-no-lambda.

import * as React from 'react';
import { DispatchProp, Dispatch, connect } from 'react-redux';
import { removeFavorite } from './../../actions/favorite';

interface ListItemProps {
  prod: Product;
  handleRemoveFavoriteClick: React.EventHandler<React.MouseEvent<HTMLButtonElement>>;
}

const ListItem: React.StatelessComponent<ListItemProps> = (props) => {
  const {
    prod,
    handleRemoveFavoriteClick
  } = props;  

  return (
    <li>
      <a href={prod.url} target="_blank">
        {prod.title}
      </a>
      <button type="button" onClick={handleRemoveFavoriteClick}>&times;</button>
    </li>
  );
};

const handleRemoveFavoriteClick = (prod: Product, dispatch: Dispatch<any>) =>
  (e: React.MouseEvent<HTMLButtonElement>) => {
    e.preventDefault();

    dispatch(removeFavorite(prod));
  };

interface FavoriteListProps {
  prods: Product[];
}

const FavoriteList: React.StatelessComponent<FavoriteListProps & DispatchProp<any>> = (props) => {
  const {
    prods,
    dispatch
  } = props;

  return (
    <ul>
      {prods.map((prod, index) => <ListItem prod={prod} key={index} handleRemoveFavoriteClick={handleRemoveFavoriteClick(prod, dispatch)} />)}
    </ul>    
  );
};

export default connect()(FavoriteList);
Run Code Online (Sandbox Code Playgroud)

如果您不熟悉打字稿,这里是 javascript 片段:

import * as React from 'react';
import { DispatchProp, Dispatch, connect } from 'react-redux';
import { removeFavorite } from './../../actions/favorite';

const ListItem = (props) => {
  const {
    prod,
    handleRemoveFavoriteClick
  } = props;  

  return (
    <li>
      <a href={prod.url} target="_blank">
        {prod.title}
      </a>
      <button type="button" onClick={handleRemoveFavoriteClick}>&times;</button>
    </li>
  );
};

const handleRemoveFavoriteClick = (prod, dispatch) =>
  (e) => {
    e.preventDefault();

    dispatch(removeFavorite(prod));
  };

const FavoriteList = (props) => {
  const {
    prods,
    dispatch
  } = props;

  return (
    <ul>
      {prods.map((prod, index) => <ListItem prod={prod} key={index} handleRemoveFavoriteClick={handleRemoveFavoriteClick(prod, dispatch)} />)}
    </ul>    
  );
};

export default connect()(FavoriteList);
Run Code Online (Sandbox Code Playgroud)