Robinson Wei
Robinson Wei

Reputation: 163

function created in React render return, good or bad

I've been asked questions like, is it good or bad to have functions in the Functional Components return an object?

like

export const Button = ({...props}) => {
    const [isChecked, setIsChecked] = useState(false);

    return (
        <div
           onClick={()=> setIsChecked(true)}>
            <div>
            ...
            </div>
        </div>
    );
};

So is it good to have this in return?

I know that on a component class if you have a function in render, it will get created every time, which tends to be a bad pattern.

But this is a functional component, it gets called every time on render, so does that brings any harm?

Upvotes: 1

Views: 794

Answers (2)

Shivam Jha
Shivam Jha

Reputation: 4522

On it's own react is pretty performant, so you will not notice any slowdowns or anything,

As T.J. Crowser pointed out,useCallback is about avoiding making child components re-render; you may want to use React.memo with useCallback hook:

const MyComponent = React.memo(function MyComponent(props) {
  /* render using props */
});

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Or, you are using some expensive calculation to set state pf a component, like Persist state with local storage, you may want to pass an updater function to useState() hook:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074839

...so does that brings any harm?

Not really, no, and it's standard practice with React hooks. JavaScript engines are very good at allocating and releasing objects, and the function's underlying code will get reused (not that there's much of it :-) ).

Hooks are actually somewhat predicated on the idea that JavaScript engines handle this well. Even useCallback still involves creating a new function every time, all useCallback does is give you back the previous version of the function if none of the dependencies changed, it can't prevent the creation (and then instant discarding) of the function you pass it.

That said, if you had a lot of these like your example that don't rely on state and wanted to avoid creating unnecessary functions each time, you could use a ref:

// If you really wanted to optimize it away
export const Button = ({...props}) => {
    const [isChecked, setIsChecked] = useState(false);
    const fnsRef = useRef(null);
    if (!fnsRef.current) {
        fnsRef.current = {
            setIsCheckedTrue: () => setIsChecked(true),
            // ...
        };
    }

    return (
        <div
           onClick={fnsRef.current.setIsCheckedTrue}>
            <div>
            ...
            </div>
        </div>
    );
};

You can use a ref when you would have put something as an instance property on the component instance with a class component. The above works because fnsRef.current will only be null the first time your function is called for a given component instance, and because the setter function you get from useState is stable.

But again, that's a micro-optimization and so doing it prematurely just complicates the code. Do it if and when you see an issue with memory allocations/cleanup slowing things down.

Upvotes: 3

Related Questions