Reputation: 163
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
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
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