I have a component that needs to call a custom React hook only when certain prop is passed. Like this:
import { useCustomHook } from './myHook';
export function Chart(props) {
...
{lots of code here}
...
if (props.canTrack) {
useCustomHook();
}
return <SomeComponent options={props.option} />
}
This is complaining with:
error React Hook "useCustomHook" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?
I was able to quickly go around this by duplicating the Chart
component into 2 Components:
import { useCustomHook } from './myHook';
export function Chart(props) {
...
{lots of code here}
...
return <SomeComponent options={props.option} />
}
export function TrackedChart(props) {
...
{lots of code here}
...
useCustomHook();
return <SomeComponent options={props.option} />
}
Which works but now I got duplicated code which is highly undesirable. In an OOP language I could use inheritance, what is the correct approach in React to avoid duplicating code or even better, be able to safely call hooks conditionally.
CodePudding user response:
Hard to give specific recommendations without knowing what the code is, but some possible fixes are:
- Write useCustomHook in a way so it can skip its work if you pass in a parameter. Then always call the hook, but change that parameter based on the prop. This is what I do most of the time when faced with a case like this.
export function Chart(props) {
...
{lots of code here}
...
useCustomHook(props.canTrack)
return <SomeComponent options={props.option} />
}
- Split the component as you've done, but extract the common code to a helper function (if hooks are called in {lots of code here}, then that helper function will be a hook itself)
function useLotsOfCode() {
...
{lots of code here}
...
// Maybe return stuff if needed
}
export function Chart(props) {
useLotsOfCode();
return <SomeComponent options={props.option} />
}
export function TrackedChart(props) {
useLotsOfCode();
useCustomHook();
return <SomeComponent options={props.option} />
}
- Split the component, but do it on a smaller scale. A parent component does all the shared code, and then you just do the split where you need the extra hook
export function Chart(props) {
...
{lots of code here}
...
if (props.canTrack) {
return <WithTheHook />
} else {
return <WithoutTheHook />
}
}
function WithTheHook () {
useCustomHook();
return <SomeComponent options={props.option} />
}
function WithoutTheHook() {
return <SomeComponent options={props.option} />
}