Suppose I have two instances of the same component. One uses a custom hook with two useState calls, and another uses a different custom hook with three useState calls. Intuitively, since they're separate instances, their state should be independent.
I came up with a sample implementation which works fine having different number of hooks for 2 different instances of the same component (MyComponent
). This is achieved via conditionally plugging a custom hook to the component body when mounting with the help of a useState
init function that guarantees picking a custom hook occurs only one time.
const [conditionalHook] = useState(() => (count % 2 === 1) ? useCustomB : useCustomA);
Is there any drawbacks?
Note: The following logic can also be used to set custom hook conditionally without a useState
init function
const conditionalHook = (count % 2 === 1) ? useCustomB : useCustomA;
But this is highly error prone as number and order of hooks can change even for a single component instance as prop values are modified
Please refer to the working sample implementation below
import { useEffect, useRef, useState } from 'react'
const useCustomA = (count) => {
const [val, setVal] = useState('');
useEffect(() => {
console.log("A Effect called");
setVal('State A' + count);
}, [count]);
return `${val}`;
};
const useCustomB = (count) => {
const [val, setVal] = useState('');
const [rnNum, setRnNum] = useState(0);
useEffect(() => {
console.log("B Effect called");
setVal('State B' + count)
setRnNum(Math.random())
}, [count]);
return `${val}-${rnNum}`;
};
const MyComponent = ({count}) => {
//Please note that conditional hook won't change between re-renders for a single component instance
//since it is determined using a useState init function,
//but can change for different instances on mounting
const [conditionalHook] = useState(() => (count % 2 === 1) ? useCustomB : useCustomA);
const value = conditionalHook(count)
return (
<>
<div>Value: {value}</div>
</>
)
};
const App = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
}
return (
<div>
<MyComponent count={count}/>
<MyComponent count={count+ 1}/>
<div>Count: {count}</div>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App
Suppose I have two instances of the same component. One uses a custom hook with two useState calls, and another uses a different custom hook with three useState calls. Intuitively, since they're separate instances, their state should be independent.
I came up with a sample implementation which works fine having different number of hooks for 2 different instances of the same component (MyComponent
). This is achieved via conditionally plugging a custom hook to the component body when mounting with the help of a useState
init function that guarantees picking a custom hook occurs only one time.
const [conditionalHook] = useState(() => (count % 2 === 1) ? useCustomB : useCustomA);
Is there any drawbacks?
Note: The following logic can also be used to set custom hook conditionally without a useState
init function
const conditionalHook = (count % 2 === 1) ? useCustomB : useCustomA;
But this is highly error prone as number and order of hooks can change even for a single component instance as prop values are modified
Please refer to the working sample implementation below
import { useEffect, useRef, useState } from 'react'
const useCustomA = (count) => {
const [val, setVal] = useState('');
useEffect(() => {
console.log("A Effect called");
setVal('State A' + count);
}, [count]);
return `${val}`;
};
const useCustomB = (count) => {
const [val, setVal] = useState('');
const [rnNum, setRnNum] = useState(0);
useEffect(() => {
console.log("B Effect called");
setVal('State B' + count)
setRnNum(Math.random())
}, [count]);
return `${val}-${rnNum}`;
};
const MyComponent = ({count}) => {
//Please note that conditional hook won't change between re-renders for a single component instance
//since it is determined using a useState init function,
//but can change for different instances on mounting
const [conditionalHook] = useState(() => (count % 2 === 1) ? useCustomB : useCustomA);
const value = conditionalHook(count)
return (
<>
<div>Value: {value}</div>
</>
)
};
const App = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
}
return (
<div>
<MyComponent count={count}/>
<MyComponent count={count+ 1}/>
<div>Count: {count}</div>
<button onClick={handleClick}>Increment</button>
</div>
);
}
export default App
Please see the following React dev tools output for these 2 instances
Share Improve this question edited Mar 8 at 19:35 Asanka Siriwardena asked Mar 8 at 17:32 Asanka SiriwardenaAsanka Siriwardena 1,0011 gold badge14 silver badges19 bronze badges 7 | Show 2 more comments1 Answer
Reset to default 0Normally React expects all the instances of a particular component should use same hooks in same order but your approach violates the assumption even though it didnt throw any react error. One of the drawback is:
Inconsistent behavior when component is re-mounted: The conditionalHook
is determined on mount so if same instance of MyComponent
mounts and then re-mounts with the different value of count
then it might use a different hook.
Example:
count = 1
then useCustomB
is choosen. count
then changes to 2
but the component does not re-mount so it still uses useCustomB
although it should use useCustomA
.
This might cause issues with further react updates that optimise rendering.
useState
init function that guarantees switching occurs only for one time when component is mountedconst [conditionalHook] = useState(() => (count % 2 === 1) ? useCustomB : useCustomA);
– Asanka Siriwardena Commented Mar 8 at 18:26<MyComponent order={Food.PIZZA} /> <MyComponent order={Food.BURGER} />
– Asanka Siriwardena Commented Mar 8 at 18:36