简体   繁体   English

在功能React组件中定义处理程序的正确方法?

[英]Correct way to define handlers in functional React components?

As far as I know, there are three ways to define functions in JavaScript. 据我所知,有三种方法可以在JavaScript中定义函数。

1. Declaration 1.宣言

function handleEvent(e) {}

2. Assignment 2.转让

var handleEvent = function(e) {}

3. Arrow 箭头

var handleEvent = (e) => {}

I have been searching for hours trying to find information on which of these options is the preferred way to declare handlers in functional React components. 我一直在寻找数小时试图找到哪些选项是在功能性React组件中声明处理程序的首选方法的信息。 All the articles I found talk about class components, binding, etc. But with the new Hooks out, there must be standard for defining them inside the function as well. 我发现的所有文章都讨论了类组件,绑定等。但是对于新的Hooks ,必须有标准来在函数内部定义它们。 (After all, functional components have always existed anyway.) (毕竟,功能组件总是存在。)

Consider the following component, which declares three handlers, which are examples of different behaviour that you might need in a React component. 请考虑以下组件,该组件声明三个处理程序,这些处理程序是React组件中可能需要的不同行为的示例。

function NameForm(props) {
    const [inputName, setInputName] = useState("");

    useEffect(() => setInputName(props.initialValue), [props.initialValue]);

    const handleInputChange = function(event) {
        setInputName(event.target.value);
    };

    const resetForm = function() {
        setInputName(props.initialValue);
        props.onReset();
    };

    const handleFormSubmit = function(event) {
        event.preventDefault();
        props.onSubmit(inputName);
        resetForm();
    };

    /* React-Bootstrap simple form example using these handlers. */
    return (
        <Form onSubmit={handleFormSubmit}>
            <Form.Group controlId="NameForm">
                <Form.Control
                    type="text"
                    placeholder="Enter your name here"
                    value={inputName}
                    onChange={handleInputChange}
                />
                <Button type="submit">Submit</Button>
                <Button onClick={resetForm}>Reset</Button>
            </Form.Group>
        </Form>
    );
}

All of these handlers are directly passed as callbacks into other components. 所有这些处理程序都作为回调直接传递给其他组件。 They might be called whenever, but at that exact moment , we need to have access to the current values of props and any state like inputName . 它们可能随时被调用,但在那个时刻 ,我们需要访问props当前值以及inputName类的任何状态。 Additionally, as you might have noticed, the handleFormSubmit handler also calls the resetForm handler. 另外,您可能已经注意到, handleFormSubmit处理程序还调用resetForm处理程序。

What would be the recommended approach to defining the handlers from a performance perspective? 从性能角度定义处理程序的推荐方法是什么? Can it be avoidable that they are redefined on every render? 是否可以避免在每次渲染时重新定义它们?

Does useCallback also fit in here somewhere? useCallback在这里也适合吗?

The current standard is to declare the handlers as constants for immutability and as arrow-functions for binding purposes. 当前的标准是将处理程序声明为不可变性的常量,并将箭头函数声明为绑定目的。

function NameForm(props) {
    const [inputName, setInputName] = useState("");

    useEffect(() => setInputName(props.initialValue), [props.initialValue]);

    const handleInputChange = (event) => {
        setInputName(event.target.value);
    }

    const resetForm = () => {
        setInputName(props.initialValue);
        props.onReset();
    }

    const handleFormSubmit = (event) => {
        event.preventDefault();
        props.onSubmit(inputName);
        resetForm();
    }

    /* React-Bootstrap simple form example using these handlers. */
    return (
        <Form onSubmit={handleFormSubmit}>
            <Form.Group controlId="NameForm">
                <Form.Control
                    type="text"
                    placeholder="Enter your name here"
                    value={inputName}
                    onChange={handleInputChange}
                />
                <Button type="submit">Submit</Button>
                <Button onClick={resetForm}>Reset</Button>
            </Form.Group>
        </Form>
    );
}

All of these handlers are directly passed as callbacks into other components. 所有这些处理程序都作为回调直接传递给其他组件。 They might be called whenever, but at that exact moment, we need to have access to the current values of props and any state like inputName 它们可能随时被调用,但在那个时刻,我们需要访问props的当前值以及inputName之类的任何状态

As currently constructed we meet the requirements for your description. 按照目前的构建,我们满足您的描述要求。 Since props and state are defined as higher level data that the component has access to, all the event-handlers have access to them. 由于propsstate被定义为组件可以访问的更高级别的数据,因此所有事件处理程序都可以访问它们。 And when used as a call-back in another component, they will remain bound to the initial component where they were defined in. 当在另一个组件中用作回调时,它们将保持与定义它们的初始组件绑定。

Which means if you have an event-handler like this: 这意味着如果你有一个像这样的事件处理程序:

const handleInputChange = (e) => {
    setValue(e.target.value)
}

And you pass it into a ChildComponent like this: 然后将它传递给像这样的ChildComponent:

<Child handleInputChange={handleInputChange}/>

And the Child uses the prop/event-handler like this: 而Child使用prop / event-handler如下:

<input onChange={props.handleInputChange}/>

You would be getting the event from the Child input, but you would be updating the state of the Parent, the original component that defined the event-handler. 您将从Child输入获取event ,但您将更新Parent的state ,Parent是定义事件处理程序的原始组件。

There is virtually no difference between the "declaration", "assignment" and "arrow" approaches. “声明”,“分配”和“箭头”方法之间几乎没有区别。 The only thing that matters is that you don't always create new instances of the handler functions on each render. 唯一重要的是你并不总是在每个渲染上创建处理函数的新实例。 For this, use the useCallback hook to memoize the function references: 为此,使用useCallback钩子来useCallback函数引用:

const handleInputChange = useCallback((event) => {
  setInputName(event.target.value);
}, []); // `setInputName` is guaranteed to be unique, from the React Hooks docs, so no need to pass it as a dependency

const resetForm = useCallback(() => {
  setInputName(props.initialValue);
  props.onReset();
}, [props.onReset, props.initialValue]; // these come from props, so we don't know if they're unique => need to be passed as dependencies

const handleFormSubmit = useCallback((event) => {
    event.preventDefault();
    props.onSubmit(inputName);
    resetForm();
}, [props.onSubmit, resetForm, inputName]); // `resetForm` and `inputName`, although defined locally, will change between renders, so we also need to pass them as dependencies

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

In functional component we don't need access to this (even more - most linters will give you warning in such a case - and for a reason!). 在功能组件中,我们不需要访问this (甚至更多 - 在这种情况下,大多数连接器会给你警告 - 并且有一个原因!)。 So it does not matter if we use arrow expression or declare a function. 因此,如果我们使用箭头表达式或声明一个函数并不重要。

But performance matters. 但性能很重要。 Whatever option from your list you choose it will be recreated on each render. 无论您选择哪个选项,都会在每个渲染上重新创建。 Declaring function itself is not really big deal but: 声明函数本身并不是很重要,但是:

  1. passed down to child it may cause unnecessary re-rendering for PureComponent/ React.memo -wrapped components. 传递给child可能会导致PureComponent / React.memo -wrapped组件不必要的重新渲染。
  2. May cause redundant run for useMemo /other useCallback / useEffect if later one includes your handler into dependencies list. 如果稍后将处理程序包含到依赖项列表中,则可能导致useMemo / other useCallback / useEffect冗余运行。

So either declare handler out of the component(once it does not rely on internal state at all) or use useCallback . 因此要么从组件中声明处理程序(一旦它完全不依赖于内部状态)或使用useCallback Beware it needs you explicitly list of all dependencies - don't ignore that. 请注意,它需要您明确列出所有依赖项 - 不要忽略它。 Otherwise result handler may operate on stale data. 否则结果处理程序可能对陈旧数据进行操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM