Lets' create a very simple hook useDouble that returns the double of a number:
export default function useDouble(nb) {
return nb * 2;
}
The documentation (https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook) reads:
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks
But if I change useDouble to double, it still works. I even tried to call other hooks from the double hook, and it still works.
Demo: https://codesandbox.io/s/laughing-gareth-usb8g?file=/src/components/WithUseDouble.js
So my question is: is naming hooks useXxxxx just a convention, or can it really break something somehow? And if it can, could you show an example?
Thanks
React hook definition according to React docs:
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.
A perfect definition of a custom hook would be (notice the removal of "use" prefix and "may"):
A custom Hook is a JavaScript function that calls other Hooks.
So we could distinguish between helper functions and custom hooks.
But, we can't tell if certain functions are actually using hooks (we do know in runtime). Thats why we use static code analyzing tools (like eslint) where we analyze text (lexical) and not meaning (semantics).
... This convention is very important. Without it, we wouldn’t be able to automatically check for violations of Rules of Hooks because we couldn’t tell if a certain function contains calls to Hooks inside of it. (source)
Hence:
// #1 a function
// CAN'T BREAK ANYTHING
function double(nb) {
return nb * 2;
}
// #2 Still a function, does not use hooks
// CAN'T BREAK ANYTHING
function useDouble(nb) {
return nb * 2;
}
// #3 a custom hook because hooks are used,
// CAN BREAK, RULES OF HOOKS
function useDouble(nb) {
const [state, setState] = useState(nb);
const doubleState = (n) => setState(n*2);
return [state,doubleState];
}
Is naming hooks useXxxxx just a convention.
Yes, to help static analyzer to warn for errors.
Can it really break something somehow?
Example #2 can't break your application since it just a "helper function" that not violating Rules of Hooks, although there will be a warning.
Could you show an example?
// #2 from above
function useDouble(nb) { return nb * 2; }
// <WithUseDouble/>
function WithUseDouble() {
// A warning violating Rules of Hooks
// but useDouble is actually a "helper function" with "wrong" naming
// WON'T break anything
if (true) {
return <h1>8 times 2 equals {useDouble(8)} (with useDouble hook)</h1>
}
return null;
}
Do I have to name my custom Hooks starting with “use”? Please do. This
convention is very important. Without it, we wouldn’t be able to
automatically check for violations of rules of Hooks because we
couldn’t tell if a certain function contains calls to Hooks inside of
it
From reactjs/docs.
And in large components that use several functions, the "use" prefix also helps in easily identifying if a function is a custom hook.
Related
why do we have to use use before custom hook. Is this just a naming convention or there is something that React is doing internally to do something special?
CODESANDBOX LINK
I've created two custom hooks useCustomHook and customHook both are defined below. One with prefix use and other without. but while using useState hook in customHook eslint gives an error as:
why It is giving an error. Since the custom hook is just a normal function in which we can use other hooks in it. Below is the rule from React docs.
Unlike a React component, a custom Hook doesn’t need to have a
specific signature. We can decide what it takes as arguments, and
what, if anything, it should return. In other words, it’s just like a
normal function. Its name should always start with use so that you can
tell at a glance that the rules of Hooks apply to it.
useCustomHook: with use Prefix
import { useState } from "react";
export default function useCustomHook(initialValue) {
const [value, setValue] = useState(initialValue);
function changeValue() {
setValue((v) => v + 100);
}
return [value, changeValue];
}
customHook: without use Prefix
import { useState } from "react";
export default function customHook(initialValue) {
const [value, setValue] = useState(initialValue);
function changeValue() {
setValue((v) => v + 100);
}
return [value, changeValue];
}
It's just a naming convention.
The docs are very slightly misleading though:
In other words, it’s just like a normal function.
It's like a normal function, but with the extra baggage that, unlike in a normal function, inside a function meant as a custom hook, you can call other hooks inside it - and if you try to call it as a plain function outside the context of React, and it uses hooks inside, it'll fail.
The part you quoted shows why the naming convention is the way it is:
Its name should always start with use so that you can tell at a glance that the rules of Hooks apply to it.
This way, it'll be much more obvious at a glance that it's a hook-invoking component that must follow the rules, rather than one that doesn't invoke hooks.
Imagine
const MyComponent = ({ checkThing }) => {
if (checkThing) {
verifyThing();
}
The above would be invalid code if verifyThing was a custom hook. If it was renamed to follow the conventions that the docs and the linter recommend:
const MyComponent = ({ checkThing }) => {
if (checkThing) {
useVerifyThing();
}
it becomes obvious that there's a problem, without even having to know anything about what verifyThing / useVerifyMean means or does. That's the main benefit of prefixing custom hooks with use - that tells you and other users of the hook that it should always be called unconditionally in the main body of a functional component, which is more useful than messing it up somewhere and accidentally calling it elsewhere and having to work backwards from a runtime error to fix it.
This is just an ESLint rule from the eslint-plugin-react-hooks plugin.
The rule, like all linter (static analysis) rules is a guide to help you produce maintainable, understandable code. It is not enforced at compilation or runtime.
For example, were you to simply disable the linter, you'd find your application runs without error
const [value, setValue] = useState(initialValue); // eslint-disable-line react-hooks/rules-of-hooks
this is a rule like other languages for example in rust or c++ we have to use semikolon in end of code so this is a react hook and when we use that must start with capitale letter
but second one is javascript function and haven't that rule
I have just found out that I can use data hooks in createSelector functions and it works. An example:
// This is a normal hook
const useUserReducer = () => {
const userAccessData = useSelector(state => state?.userAccessData)
return userAccessData
}
// Here I use the hook as first argument!
export const useUserReducerFromCreateSelector = createSelector(useUserReducer, (result) => {
console.log(result) // userAccessData printed correctly
return result
})
Then I use it in my component as a normal hook:
const Component = () => {
const result = useUserReducerFromCreateSelector([])
console.log(result) // userAccessData printed correctly
return (
<>
{JSON.stringify(result)}
</>
)
}
I dont see any documentation about this, so I wonder if its safe to use it. It would help me a lot creating reusable selectors.
(I tested while changing the state at various points in time and I always see the correct state)
It is certainly an abuse if it is working. createSelector is only supposed to be a pure state selector function, so naming the returned selector function like a React hook, i.e. useUserReducerFromCreateSelector, is likely to cause some linter warnings eventually.
The potential issue is that Reselect and createSelector creates memoized selector functions. If the input value to a selector doesn't change, then the selector function returns the previously computed selector value. This means that a selector using a React hook like this potentially conditionally calling a React hook which is a violation of the Rules of Hooks.
Only Call Hooks at the Top Level
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function,
before any early returns. By following this rule, you ensure that
Hooks are called in the same order each time a component renders.
That’s what allows React to correctly preserve the state of Hooks
between multiple useState and useEffect calls. (If you’re curious,
we’ll explain this in depth below.)
Only Call Hooks from React Functions
Don’t call Hooks from regular JavaScript functions. Instead, you
can:
✅ Call Hooks from React function components.
✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).
By following this rule, you ensure that all stateful logic in a
component is clearly visible from its source code.
I don't consider it safe to use any React hook in a selector function like this.
Split out the logic of selecting the state from the useUserReducerFromCreateSelector hook to be used in your selector functions.
Example:
const userAccessData = state => state?.userAccessData || {};
const computedUserAccessData = createSelector(
[userAccessData],
data => {
// logic to compute derived state, etc...
return newUserAccessData;
},
);
I was really intrigued seeing this particular use in the redux-toolkit github repo issues and nobody complaining about it so I decided to ask the same question in the reselect github page.
Here is the response of Mark Erikson (redux maintainer):
No, this is not safe!
You're technically getting away with it because of how you're using
that in a component. But if you were to try to use that selector
outside of a component, it would break.
I'd really recommend sticking with keeping these concepts separate.
Write and name selectors as selectors. Write and name hooks as hooks.
Don't try and mix the two :)
To be clear, the code that you wrote above should run. It's ultimately
"just" composition of functions and calling them in a particular
order.
But given how hooks work, and how selectors work, it's best to keep
those concepts separate when writing the code to avoid confusion.
I am currently rewriting my website into a webapp with ReactJS, and have a little trouble using and understanding hooks. I wrote a custom hook "useFetch" that works, never had any problems with it until now : I am currently trying to use my hook in a function like this :
import useFetch from '../../../hooks/useFetch';
import {clientGenerateProcessScreen, clientClearProcessScreen} from '../../../utils/processScreens';
function myFunction (paramName, paramType, paramDesc) {
let process_screen = clientGenerateProcessScreen ();
let rqt_url = `/fileNameA.php`;
if (paramType!= "a") rqt_url = `/fileNameB.php`;
const { data, isLoading, error } = useFetch(rqt_url);
if (!isLoading && data.success) {
doSomethingA ();
} else {
showErrorMessage ();
}
}
export default myFunction;
The function is called from an onClick on a react component. It should theorically work fine, however, I always end up having this error :
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
I do not understand where the error here is. I am using my hook at the top of a function, not in a condition or anything that might break hook rules. I have tried importing React and ReactDOM in my function file, but this doesn't solve any of my issues. I am guessing I might have missed something basic or simple, yet can't find what...
Firstly, you implemented a custom hook, therefore it should be prefixed with the "use" word: useMyFunction, for more context see Do React hooks really have to start with "use"?.
Now that you know its a hook and not a util function, it must follow hooks API ("Rules of Hooks"), and one of those rules is that you must call it in a top level, i.e You CANT use it as a callback.
For solving it, it requires a logical change, its not something you have a step-by-step fixing guide, rethink its logical use.
I just started using hooks in react and am creating a prototype custom hook for a framework.
The hook should take an object as an argument for initialization and cleanup (setting up/removing callbacks for example).
Here is my simplified Code so far:
export function useManager(InitObj) {
const [manager] = useState(() => new Manager());
useEffect(() => {
manager.addRefs(InitObj)
return () => manager.removeRefs(InitObj)
}, [manager]);
return manager;
}
to be used like this:
useManager({ cb1: setData1, cb2: setData2... })
In future Iterations the Manager might be a shared instance, so I need to be able to be specific about what I remove upon cleanup.
I put console.log all over the place to see If i correctly understand which code will be run during a render call. From what I can tell this code does 100% what I expeted it to do!
Unfortunately (and understandably) I get a warning because I did not include InitObj in the effects dependencies. But since I get an object literal simply putting it in there will cause the effect to be cleaned up/rerun on every render call since {} != {} which would be completely unnecessary.
My research so far only revealed blog posts like this one, but here only primitive data is used that is easily classified as "the same" (1 == 1)
So far I have found 3 possible solutions that I am not completely happy with:
using useMemo to memoize the object literal outside the hook
useManager(useMemo(() => { cb: setData }, []))
This adds more responsibility on the developer using my code => not desirable!
using useState inside the hook
const [iniOBj] = useState(InitObj);
A lot better already, but it adds state that does not feel like state. And it costs (minimal) execution time and memory, I would like to avoid that if possible.
using // eslint-disable-next-line react-hooks/exhaustive-deps
Works for sure, but there might still be other dependencies that might be missed if I simply deactivate the warning.
So my question is:
How can I use an object as initializer for custom hooks without adding complexity/state or inviting future problems?
I half expect that the useState option will be my best choice, but since I am new to hooks there might still be something that eluded my understanding so far.
How can I detect if a passed function is a React Hook function?
isHook(useState); // return true
isHook(() => {}); // return false;
I need to know is attached function to a object property is a hook or not. I need to call the function if its a "normal function" from another function.
Built-in hooks can be identified by comparing them against known React hooks,
[React.useState, React.useReducer, /*...*/].includes(hookFn);
There is no way to know if a function is custom hook, i.e. a function that calls React built-in hooks internally.
If there's a chance that a function can be custom hook, it should be called accordingly to hook rules, i.e. to be unconditionally called inside functional component.
React custom hooks conventionally have use... names, this way they can be identified by a developer by sight. They should never be identified programmatically by their names in client side applications because original function name is lost during minification and may never exist at all, depending on how a function was defined.
Little late to the party here, but hope this helps.
React blindly considers any function that is named ("use" followed by a capitol letter) a Hook.
Knowing this, you can take a reference to a function, get it's name as a string, and apply the same blind matching.
const useMyCustomHook = () => {...}
useMyCustomHook.name; // "useMyCustomHook"
const isAHook = (ref) =>
ref && typeof(useTest) === 'function' && /^use[A-Z]+/.test(ref.name)
isAHook(useMyCustomHook); // true