admin管理员组

文章数量:1287597

As far as I know, there are three ways to define functions in JavaScript.

1. Declaration

function handleEvent(e) {}

2. Assignment

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 ponents. All the articles I found talk about class ponents, binding, etc. But with the new Hooks out, there must be standard for defining them inside the function as well. (After all, functional ponents have always existed anyway.)

Consider the following ponent, which declares three handlers, which are examples of different behaviour that you might need in a React ponent.

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 ponents. 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. Additionally, as you might have noticed, the handleFormSubmit handler also calls the resetForm handler.

What would be the remended 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?

As far as I know, there are three ways to define functions in JavaScript.

1. Declaration

function handleEvent(e) {}

2. Assignment

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 ponents. All the articles I found talk about class ponents, binding, etc. But with the new Hooks out, there must be standard for defining them inside the function as well. (After all, functional ponents have always existed anyway.)

Consider the following ponent, which declares three handlers, which are examples of different behaviour that you might need in a React ponent.

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 ponents. 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. Additionally, as you might have noticed, the handleFormSubmit handler also calls the resetForm handler.

What would be the remended 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?

Share Improve this question edited Aug 3, 2019 at 9:36 Snackoverflow asked Aug 3, 2019 at 9:22 SnackoverflowSnackoverflow 6,2818 gold badges44 silver badges74 bronze badges 2
  • What you have currently isnt valid javascript. If you intialize a const, you cant just define a function without first setting the const equal to something. – Cat_Enthusiast Commented Aug 3, 2019 at 9:30
  • Ah thanks, it was a typo. – Snackoverflow Commented Aug 3, 2019 at 9:35
Add a ment  | 

3 Answers 3

Reset to default 5

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 ponents. 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

As currently constructed we meet the requirements for your description. Since props and state are defined as higher level data that the ponent has access to, all the event-handlers have access to them. And when used as a call-back in another ponent, they will remain bound to the initial ponent 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:

<Child handleInputChange={handleInputChange}/>

And the Child uses the prop/event-handler like this:

<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 ponent that defined the event-handler.

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:

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 e 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/docs/hooks-reference.html#usecallback

In functional ponent we don't need access to this(even more - most linters will give you warning in such a case - and for a reason!). 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 ponents.
  2. May cause redundant run for useMemo/other useCallback/useEffect if later one includes your handler into dependencies list.

So either declare handler out of the ponent(once it does not rely on internal state at all) or use useCallback. Beware it needs you explicitly list of all dependencies - don't ignore that. Otherwise result handler may operate on stale data.

本文标签: javascriptCorrect way to define handlers in functional React componentsStack Overflow