8

I have been using react hooks more but one thing that I have found to be a pain is needing to pass a lot of variables into my utility functions because I don't have access to them anymore. For example say I have a component like this:

    export const ListItem(props) {
        const {id, item, itemStatuses, workflows, taks} = props
        const [checked, setChecked] = useState(false)
        return (
            <CheckboxItem
                key={id}
                title={item.title}
                onPress={() => handleCheckboxPress(id, item, itemStatuses, 
                         setChecked, workflows)}
            /> 
        )
}

And the handleCheckboxPress is a function outside of the component that runs a bunch of logic and needs all (or most of) the props from the component to figure out some state, and then also needed to setState callback as well to be able to change some internal state.

In the past I would just make them methods of the class and I would have access to all props and setStates, etc.

Is there some pattern to avoid having to pass all of these props into a bunch of utility functions?

I have read that putting all of this logic in functions INSIDE the component is bad for performance because react recreates all of those functions on every render. Is this true or is that a valid pattern because that would solve my pain point.

Travis James
  • 1,879
  • 9
  • 22
  • 1
    I mean why dont you just pass `props` instead of each prop individually? or `...props` to pass them all through? If you dont want to re-create the functions you can memoize them. `React.memo` – John Ruddell Aug 27 '19 at 18:09
  • Would that cause issues if some of the props are big objects or arrays and my function doesn't need them? Or is it negligible performance wise? – Travis James Aug 27 '19 at 18:10
  • Ahhh thanks for the React.memo tip. I haven't looked at it much but I will right now. And then I would able to write the functions within the component? – Travis James Aug 27 '19 at 18:11
  • IMO it's a bad practice to have function with a lot of parameters, is it possible to split your function in multiples small function ? – Olivier Boissé Aug 27 '19 at 18:16
  • yes, you can use memo / usecallback to define them in the render function essentially. I'd reference this https://stackoverflow.com/a/56375813/2733506 – John Ruddell Aug 27 '19 at 18:17
  • Awesome, thanks a lot for that link! – Travis James Aug 27 '19 at 18:19
  • @TravisJames could you post the actual `handleCheckboxPress`? Because it might be that you need to memoize parts of it. – Alvaro Aug 27 '19 at 20:01

1 Answers1

3

In your example you'd want to use useCallback

export const ListItem(props) {
    const {id, item, itemStatuses, workflows, taks} = props
    const [checked, setChecked] = useState(false)
    const handlePress = useCallback(() => {
      // I have access to props now
    }, [id, item /* ... */ ])
    return (
        <CheckboxItem
            key={id}
            title={item.title}
            onPress={handlePress}
        /> 
    )
}
John Ruddell
  • 25,283
  • 6
  • 57
  • 86
  • Just wondering, shouldn't `handleCheckboxPress` be defined inside `useCallback`? Otherwise it will be created every render and thus not take advantage of `useCallback`. – Alvaro Aug 27 '19 at 18:28
  • 2
    I don't understand why the answer was accepted. `useCallback` will not prevent creating a new function at each render, so it will not improve the performance, actually you created the new function when passing it in `useCallback` arguments. `useCallback` is usefull when you don't want the function reference to be changed, for example when you pass the handler to a child pure component – Olivier Boissé Aug 27 '19 at 19:03
  • 2
    What do you suggest to solve the problem then @OlivierBoissé? If you write an answer that better answers the question I am willing to change it... – Travis James Aug 27 '19 at 19:43
  • @OlivierBoissé the second param of `useCallback` is the dependencies on which you want to re-create the function. This will limit the number of re-creations of a handler function. Not exactly sure what you mean, if you can provide an example to explain what you mean. [**Here is what im talking about**](https://codesandbox.io/s/react-example-mwigr). If you comment out the handlers that use `useCallback` and try the normal ones you'll see the difference. – John Ruddell Aug 27 '19 at 20:36
  • the `handlePress` will keep the same reference as you pass `[]` in the second argument, but keep in mind you are passing a new function in the first argument, so behind the scene you are creating a function at each render even if it will not be assigned to the `handlePress` variable. The OP didn't want to create a new function at every render for performance reason – Olivier Boissé Aug 28 '19 at 07:22
  • I provided an example https://usecallback-function-creation.stackblitz.io to explain my previous comment – Olivier Boissé Aug 28 '19 at 07:42
  • That doesn't matter the reference doesn't change. The whole reason is for potential performance issues... It's the same reference so it's not a performance issue. – John Ruddell Aug 28 '19 at 14:39
  • @OlivierBoissé Also, your example you posted you're doing the same thing? you're creating a function every render cycle. Actually, your example is worse.. [**if you make more than one state variable**](https://codesandbox.io/s/react-example-1t40y), updates to one creates the second function again. So you lose performance :/ – John Ruddell Aug 28 '19 at 21:44
  • @OlivierBoissé here's a [**side by side comparison**](https://codesandbox.io/s/react-example-s0z3m) of what I did and what you did. Not sure what you're talking about dude.. your recommendation is actually worse. – John Ruddell Aug 28 '19 at 22:05