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
Related
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.
It's a known React behavior that code runs twice.
However, I'm creating a form builder in which I need to be able to give each form input a dynamic Id and use that Id for a lot of other purposes later. Here's a simple code of an input:
const Text = ({placeholder}) => {
const [id, setId] = useState(Math.random());
eventEmitter.on('global-event', () => {
var field = document.querySelector(`#${id}`); // here, id is changed
});
}
But since Math.random() is a side-effect, it's called twice and I can't create dynamic ids for my form fields.
The reason I'm using document.querySelector can be read here.
My question is, how can I create consistent dynamic ids for my inputs?
It seems you think that useState(Math.random()); is the side-effect causing you issue, but only functions passed to useState are double-invoked.
I think the issue you have is that the eventEmitter.on call is the unintentional side-effect since the function component body is also double invoked.
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies <-- this
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer <-- not this
To remedy this I believe you should place the eventEmitter.on logic into an useEffect hook with a dependency on the id state. You should also probably use id values that are guaranteed a lot more uniqueness. Don't forget to return a cleanup function from the effect to remove any active event "listeners", either when id updates, or when the component unmounts. This is to help clear out any resource leaks (memory, sockets, etc...).
Example:
import { v4 as uuidV4 } from 'uuid';
const Text = ({placeholder}) => {
const [id, setId] = useState(uuidV4());
useEffect(() => {
const handler = () => {
let field = document.querySelector(`#${id}`);
};
eventEmitter.on('global-event', handler);
return () => {
eventEmitter.removeListener('global-event', handler);
};
}, [id]);
...
}
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.
Is it possible to enforce a specific order in which React hooks are used? For example some linting/prettier settings that will always make sure to first use 'useState' hooks and only after that use 'useEffect' hooks. Let's say I have the following code:
const MyComponent = () => {
const [myState1, setMyState1] = React.useState();
const [myState2, setMyState2] = React.useState();
React.useEffect(() => {
runSomeEffect()
}, [])
const [myState3, setMyState3] = React.useState();
return <div>...</div>
}
I would like to have some sort of warning that the third useState hooks should be moved to the top. I looked into Eslint and Prettier, but I don't think any of those have rules to enforce the order of React hooks.
You can't have such rule as the call order of hooks matters due to Rules of Hooks.
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.
You have an in-depth explanation of why, here.
React relies on the order in which Hooks are called
Therefore such a rule may cause unexpected behavior where the developer would enforce order and the rule will break it.
So I have started using React hooks now. I have been experimenting with the API for some time now. i really like the idea of bringing the state to functional components. but there is this one thing which keeps on bothering me and it doesn't feel right in the gut when i am trying to use it. I tried posting on RFCs but it's too crowded there now. everything seems lost there.
Here is a piece of code from my example.
import React, { useState } from "react";
function Counter() {
const [counterState,incrementCounterState] = useCommontState(0);
function doSomething (){
// does something and then calls incrementCounterState
// with the updated state.
}
return (
<div>
<p>{counterState}</p>
<button onClick={incrementCounterState}>increase</button>
....
.... // some jsx calling local scoped functions.
....
</div>
);
}
function useCommontState(defaultValue){
var [state, setState] = useState(0);
function increment(){
setState(defaultValue+=1);
}
return [state, increment]
}
export default Counter;
I can easily take out state and setState methods out and create a custom hook but my problem is with the local functions that are used by the component. since state is now part of the component there will be cases where some logic will decide what to do next with the state.
Also, when the component re-renders on state change everything gets reinitialized. which is my problem. I know that useState has its own way of handling the issue. but my problem is with my own functions. the click handlers. on change events, callbacks for child components etc. all that will be reinitialized everytime the component renders. this doesn't feel right to me.
Are there any ways by which we can work around it. it's a new API. we are not even sure if it will make into react 17. but has anyone come across any better way to do it?
I had the same concerns as well when I first saw the proposal, but this was addressed in the React Docs Hooks Proposal FAQ:
Are Hooks slow because of creating functions in render?
No. In modern browsers, the raw performance of closures compared to classes doesn’t differ significantly except in extreme scenarios.
My takeaway is that although you have additional overhead now in the repeated declarations per render, you have additional wins elsewhere:
Hooks avoid a lot of the overhead that classes require, like the cost of creating class instances and binding event handlers in the constructor.
Idiomatic code using Hooks doesn’t need the deep component tree nesting that is prevalent in codebases that use higher-order components, render props, and context. With smaller component trees, React has less work to do.
Overall the benefits might be more than the downsides which makes hooks worth using.
You can always simplify the code to take functions out so that they aren't initialised always, by passing the required values as constants.
import React, { useState } from "react";
function doSomething (counterState, incrementCounterState){
// does something and then calls incrementCounterState
// with the updated state.
}
function Counter() {
const [counterState,incrementCounterState] = useCommontState(0);
return (
<div>
<p>{counterState}</p>
<button onClick={incrementCounterState}>increase</button>
....
.... // some jsx calling local scoped functions.
....
</div>
);
}
function increment(defaultValue, setState){
setState(defaultValue + 1);
}
function useCommontState(defaultValue){
var [state, setState] = useState(0);
return [state, increment]
}
export default Counter;
Also in my opinion the function design being suggested in all the demos and docs is for people to get comfortable with it and then think about the re-initialization aspects. Also the cost that re-initialization would significanly be overpowered by the other benefits that it provides.
I'm using createOnce helper function to prevent reinitialises, But I'm not sure if it's correct or not.
utils/createOnce.js
import { useMemo } from 'react';
export const createOnce = toCreate => useMemo(() => toCreate, []);
SomeComponent.js
...
const someFunction = createOnce((counter) => {
// whatever
return counter + 1;
});
...