Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difficulty understanding the value of removing arrow functions () =>

When I search the internet for react-native optimizations / best practices (Especially for FlatLists which are often greedy), I always find the advice not to use the arrow functions <Component onPress={() => ... }.

Example 1 : https://reactnative.dev/docs/optimizing-flatlist-configuration#avoid-anonymous-function-on-renderitem :

Move out the renderItem function to the outside of render function, so it won't recreate itself each time render function called. (...)

Example 2 : https://blog.codemagic.io/improve-react-native-app-performance/ :

Avoid Arrow Functions : Arrow functions are a common culprit for wasteful re-renders. Don’t use arrow functions as callbacks in your functions to render views (...)

Example 3 : https://medium.com/wix-engineering/dealing-with-performance-issues-in-react-native-b181d0012cfa :

Arrow functions is another usual suspect for wasteful re-renders. Don’t use arrow functions as callbacks (such as click/tap) in your render functions (...)

I understand that it is recommended not to use arrow function (especially in onPress button and FlatList), and to put the components outside of the render if possible.

Good practice example :

const IndexScreen = () => {    
  const onPress = () => console.log('PRESS, change state, etc...')

  return (
    <>
      <Button
        onPress={onPress}
      />
      <FlatList
        ...
        renderItem={renderItem}
        ListFooterComponent={renderFooter}
      />
    </>
  )
}

const renderItem = ({ item: data }) => <Item data={data} ... />
const renderFooter = () => <Footer ... />

export default IndexScreen

But, often, I have other properties to integrate into my child components. The arrow function is therefore mandatory:

const IndexScreen = () => {
  const otherData = ...(usually it comes from a useContext())...

  <FlatList
    ...
    renderItem={({ item: data }) => renderItem(data, otherData)}
  />
}

const renderItem = (data, otherData) => <Item data={data} otherData={otherData} />

export default IndexScreen

In the latter situation, are good practices followed despite the presence of an arrow function ? In summary, if I remove otherData (for simplicity), are these two situations strictly identical and are good practices followed ?

Situation 1 :

const IndexScreen = () => {    
  return (
    <FlatList
      ...
      renderItem={renderItem}
    />
  )
}

const renderItem = ({ item: data }) => <Item data={data} ... />

export default IndexScreen

=== Situation 2 ?

const IndexScreen = () => {    
  return (
    <FlatList
      ...
      renderItem={({ item: data }) => renderItem(data)}
    />
  )
}

const renderItem = (data) => <Item data={data} ... />

export default IndexScreen
like image 392
Gaylord.P Avatar asked Oct 18 '21 14:10

Gaylord.P


2 Answers

The answer has nothing to do with arrow functions, but rather understanding reference equality why react might decide to rerender a component.

You can use useCallback to wrap your function. This will cause the reference to renderItem to only update when one of your callback dependencies updates.

const renderItem = useCallback(()=>{
...
},
[otherdata]);
like image 138
windowsill Avatar answered Sep 28 '22 02:09

windowsill


The first situation is ideal, because when your app code runs it will create only one renderItem function. In the second situation, even if it doesn't have otherProps, you're not following the good practice because a new function is created every render in this line

renderItem={({ item: data }) => renderItem(data)}

Hence, making the FlatList rerender every time.

To fix it, you need to memoize the function you pass in the renderItem prop with useCallback

const renderItem  = useCallback(({ item: data }) => {
   return (<Item data={data} />)
}, []);

...

    <FlatList
      ...
      renderItem={renderItem}
    />

so the memoized version will be created only once when the component mounts. And, if you need to inject more data in the render function, you define that data as a dependency of the useCallback hook to create the function only when that data changes, reducing rerenders down the tree.

const renderItem  = useCallback(({ item: data }) => {
   return (<Item data={data} otherData={otherData} />)
}, [otherData]);
like image 31
diedu Avatar answered Sep 28 '22 02:09

diedu