Reputation:
Can I pass React.hooks to a function and use it there?
const [X, setX] = useState('');
function Y(useX){
useX('string');
}
Y(setX)
Upvotes: 7
Views: 16480
Reputation: 16716
When using hooks, you must abide to certain hook rules (there are only two at the moment):
By attempting to pass a hook to a regular function you are violating both of them. And while these rules are more of conventions, than actual restrictions and might work (as others in this thread have mentioned) in some cases (either you knowing where and why you can use them or you accidentally fulfilling their usage requirements) it is not recommended to use them in such way. For example, after some time you might forget that your function contains a hook and actually depends on where and how it is declared and used, and move it around, or put into a conditional, effectively breaking the logic of your application.
It is so important that React folks came up with the dedicated ESlint plugin that is meant to analyze your code in the background and warn you exactly when you are close to violating hook rules. It's the way to know for sure.
If you still need to extract some hook dependent logic from your component to a separate function, consider creating a custom hook.
BUT!
In your sample code you are not passing a hook to a function. React.useState
is a hook, but the setX
that it returns is not. You can pass it around however you like, and you actually are meant to pass it to your custom made regular functions or custom hooks!
Upvotes: 17
Reputation: 281646
As the rule states, hooks can only be called at the top of the functional component and shouldn't be called conditionally, so while technically you can pass it as an argument of a function, but then you would need to execute the function immediately
So
const App = () => {
const [X, setX] = React.useState('x');
function Y(useX){
useX('string');
}
Y(React.useState);
return <div>Hello {X}</div>
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />
would be okay.
But below code won't work
const App = () => {
const [X, setX] = React.useState('x');
function Y(useX){
useX('string');
}
return <div>Hello {X} <button onClick={() => {Y(React.useState)}}>Click</button></div>
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app" />
Hence you should not use it this way because it may so happen that you later add some statements within function calls and hence the hook is not preserving the order of its call
Maintaining the order of Hooks invocation is required because React relies on hooks call order to maintain a queue of hooks being used in the method and process all further updates
Technically if
useX
is a custom hook, it essentially a function that is invoked from within a component that contains hooks at the top level
Upvotes: 0
Reputation: 222344
In this case it's not a hook but setter function that is passed, hook limitations aren't applicable to to Y(setX)
. It shouldn't necessarily reside in component scope:
function Y(setX){
setX('string');
}
const Comp = () => {
const [X, setX] = useState('');
Y(setX);
...
}
Depending on the usage it can benefit from memoization, for instance if () => Y(setX)
should be passed as a prop:
const Comp = () => {
const [X, setX] = useState('');
const setY = useCallback(() => Y(setX), []);
...
}
Upvotes: -1