Reputation: 854
I have a functional component (which previously was class-based but I decided to refactor it with react hooks). So now every function which is declared inside will be declared on every rerender (In this example handleClick
). When it was a class-based component such problem did not exist because the function was stored in the class property.
So, my question is where do such functions belong to?
Before:
class Select extends Component {
constructor(props) {
super(props)
state ={
value: props.value
}
}
render() {
return <OtherComponent click={this.handleClick} />
}
handleClick = (value) => {
this.setState({value})
}
}
After:
const Select = (props) => {
const [value, setValue] = useState(props.value);
return <OtherComponent click={handleClick} />
function handleClick(value) {
setValue(value)
}
}
Upvotes: 1
Views: 981
Reputation: 31495
Those functions belong inside your main component function declaration's body.
It's weird knowing that they'll be recreated on every render, but that is the way it's meant to be done.
useCallback
is a way that you could optmize the function re-creation, but unless you're building extremely expensive components, you'll probably never have any performance issues on this subject.
From React Docs, we get that:
https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render
Are Hooks slow because of creating functions in render?
No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.
In addition, consider that the design of Hooks is more efficient in a couple ways:
Hooks avoid a lot of the overhead that classes require, like the cost of creating class instances and binding event handlers in the constructor.
Idiomatic code using Hooks doesn’t need the deep component tree nesting that is prevalent in codebases that use higher-order components, render props, and context. With smaller component trees, React has less work to do.
Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks shouldComponentUpdate optimizations in child components. Hooks approach this problem from three sides.
The useCallback Hook lets you keep the same callback reference between re-renders so that shouldComponentUpdate continues to work:
// Will not change unless `a` or `b` changes
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Upvotes: 2
Reputation: 18288
You can use the useCallback
hook to memoize the function so it is only recreated when its dependencies change.
Something like this:
const Select = (props) => {
const [value, setValue] = useState(props.value);
const memoizedHandleClick = useCallback(
(value) => {
setValue(value)
},
);
return <OtherComponent click={memoizedHandleClick} />
}
I'd highly recommend reading the the full hooks reference, paying special attention to the function form of the useState updater (ie: setValue(x => x + 1)
) and the second argument to useCallback which lists the dependencies of your memoized function.
Reference: https://reactjs.org/docs/hooks-reference.html#usecallback
Upvotes: 0