I have a compound component that is used to build a bunch of different (but similar) screens in a react native app. Data comes from a CMS, and depending on the value of a prop called type it needs to have different state variables, different validation, etc. I've written a config object who's methods map to the values of type, and contain the state and functions needed for each use case. The pattern looks like this (edited for example sake):
import { useState } from 'react';
const MyComponent = props => {
const { type } = props; // possible values of type are 'A', 'B', 'C'
const config = {
A() {
const [value, setValue] = useState('');
function onChange({ target }) {
setValue(target.value);
}
return {
value,
onChange
}
},
B() {
// ...
return {
value: '',
onChange: () => {}
}
},
C() {
// ...
return {
value: '',
onChange: () => {}
}
}
}[type]();
return <input value={config.value} onChange={config.onChange} />
};
export default MyComponent;
BUT!
In the react docs it says this:
Don’t call Hooks inside loops, conditions, or nested functions.
My question is - does the above example violate the rules of hooks? It seems to work for what I need it to, but I suspect that it may cause problems. Would appreciate any thoughts / discussion. Cheers!
CodePudding user response:
Short answer: yes.
Long answer:
React depends on hooks to be called in the exact same order between renders (I believe internally the hooks are stored in a linked list, which is altered on mounting and unmounting of the component, if the order changes between renders without a mount and unmount, React has no way to know which hook belongs to which component).
Because your rendering from a CMS, the value of type is unlikely to change between renders, so you probably will never get in to trouble with this particular use case, but it is a bad habit to get in to, and there is almost certainly a better way to handle what you are trying to do. Maybe write a hook that takes type as an argument?
function useMyHook(type:string) {
const [state, setState] = useState('');
const onChange = useMemo(() => {
if (type = 'A')
return () => doXyz()
}, [type])
return {state, onChange}
}
This is contrived, but hopefully will get you thinking of alternate approaches to your problem.
