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:
EDIT:
The function to calculate the path depends on some props I am passing from the parent component.
To run the useEffect hook callback only once when the component mounts, we just have to pass in an empty array into the 2nd argument of useEffect hook. We just pass in an empty array into useEffect and the callback would only run once.
While there's currently no React Hook that does this out of the box, you can manually retrieve either the previous state or props from within a functional component by leveraging the useRef , useState , usePrevious , and useEffect Hooks in React.
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.
An even more elegant option is to use the callback form of useState
. If you pass it a function, that function will be called only when the initial state needs to be calculated - that is, on the initial render:
const [path, setPath] = React.useState(heavyCalculation);
const heavyCalculation = () => {
console.log('doing heavy calculation');
return 0;
};
const App = () => {
const [path, setPath] = React.useState(heavyCalculation);
React.useEffect(() => {
setInterval(() => {
setPath(path => path + 1);
}, 1000);
}, []);
return 'Path is: ' + path;
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class="react"></div>
As you can see in the snippet above, doing heavy calculation
only gets logged once.
You can use the hook useMemo, it returns the value to your const, it receives the function that returns the value and also an array, send an empty one to calculate just once, you can send some dependencies, props or state if needed to recalculate the value when they change.
In your case you can do something like this:
const Figure = (props) => {
const path = React.useMemo(() => {
// calculate and return value for path
}, []);
return(
<G>
<Path d={path} />
</G>
)
}
It was created for that porpuse.
Hope it helps ;)!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With