Search code examples
reactjsreact-nativereact-hooksreact-state-managementreact-native-svg

How to ensure a constant inside a component is only calculated once on start in React (Native) with hooks?


I am currently creating a React Native app and still am unsure about how to best handle states for my use case.

I use react-native-svg to create a rather complex figure in the app, which is stored in a component, stays constant and is retrieved in a parent component. This parent component is then regularly rotated and translated on the canvas, but the path of the complex figure does not change. Therefore, and because the calculations are rather heavy, I would like to calculate the SVG path of that figure only on the start of the app, but not whenever I rotate or translate the parent component. Currently, my code structure looks like this:

Figure Component

const Figure = (props) => {
    const [path, setPath] = useState({FUNCTION TO CALCULATE THE SVG PATH});

    return(
        <G>
            <Path d={path} />
        </G>
    )
}

Parent Component

const Parent = (props) => {
    return (
        <View>
            <Svg>
                <G transform={ROTATE AND TRANSFORM BASED ON INTERACTION}>
                    <Figure />
                </G>
            </Svg>
        </View>
    )
}

As you can see I am using a state for the path data. My questions now:

  1. Am I ensuring here that this path is only calculated once on start?
  2. Is there a more elegant/better way of doing this (e.g. I am not using the setter function at all right now, so I don't feel like this is the best practice)

EDIT:

The function to calculate the path depends on some props I am passing from the parent component.


Solution

  • When you pass a value to useState that value is used to initialize the state. It does not get set every time the component rerenders, so in your case the path is only ever set when the component mounts.

    Even if the props change, the path state will not.

    As your initial state depends on a prop, all you need to do is pull the relevant prop out of your props and pass it to the function that calculates the initial path value:

    const Figure = (props) => {
      // get relevant prop
      const { importantProp } = props;
    
      // path will never change, even when props change
      const [path] = useState(calculatePath(importantProp));
    
      return(
        <G>
          <Path d={path} />
        </G>
      )
    }
    

    However, the calculatePath function still gets evaluated every render, even though the path value is not re-initialized. If calculatePath is an expensive operation, then consider using useMemo instead:

    You can ensure that path is updated only when a specific prop changes by adding that prop to the dependency array:

    const Figure = (props) => {
      const { importantProp } = props;
    
      const path = useMemo(() => calculatePath(importantProp), [importantProp]);
    
      return(
        <G>
          <Path d={path} />
        </G>
      )
    }
    

    And in that case, you don't need state at all.

    Adding the importantProp to the useMemo dependency array means that every time importantProp changes, React will recalculate your path variable.

    Using useMemo will prevent the expensive calculation from being evaluated on every render.

    I've created a CodeSandbox example where you can see in the console that the path is only re-calculated when the specified prop important changes. If you change any other props, the path does not get recalculated.