How can I implement a `isHook` function? - javascript

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

Related

Is it safe to use hooks in createSelector?

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.

Do React hooks really have to start with "use"?

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.

How can I use an object as initializer for custom hooks without adding complexity/state or inviting future problems?

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.

Calling a method vs using a function to call a method

Suppose we have a method inside a class like this
class Blog extends Component {
postClicked = (id) => {
this.setState({selectedPostId: id})
}
render () {
const newPosts = this.state.posts.map(el => {
return <Post key={el.id}
title={el.title}
author={el.author}
onClick={this.postClicked(el.id)}/>
})
return
//something
{post}
}
}
}
Now, What is the difference between calling the handler like this
onClick={this.postClicked(el.id)} and onClick={() => this.postClicked(el.id)}
Would appreciate if someone can tell me the difference in general
after Ecmascript 6 javascript was introduced with is arrow function link
here ()==>{//code} is a similar as a function() or anonymous function
tell me if you find out what you want
The first option, "this.postClicked(el.id)", will actually call the method, "this.postClicked", with the "el.id" argument, each time the component renders (probably not what's intended).
The second option, "() => this.postClicked(el.id)", will only call the method, "this.postClicked", with the "el.id" argument, when "Post" is clicked.
Overall, if you can find a way to put the "el.id" argument into an "id" or "name" prop on the component
<Post id={el.id} />
then you can do:
<Post
id={el.id}
onClick={this.postClicked}
/>
this.postClicked = (event) => {
const { id } = event.target;
...
}
This last option avoids the use of an unnamed function. If you use an unnamed function, it will cause unnecessary re-renders. React cannot tell that an unnamed function is the same when it's checking whether or not it should re-render, by considering if the props of a component have changed. It considers the unnamed functions to be a new prop each time it checks, causing an unnecessary re-render each time.
Overall, it won't break your app, but it slows down performance slightly if you do it enough. It comes up especially if you start using React Motion (you'll really notice a difference there). It's best to avoid unnamed functions if possible.
you can read this blog it wil clear the things https://medium.com/#machnicki/handle-events-in-react-with-arrow-functions-ede88184bbb
Differences are,
First method is a wrong implementation and it wont give the intended result, where as second one will work.
In the first method you are making a function call, in second one you are assigning a function's signature to onClick.
It is like the combination of below two statements.
var variableName = function(){//some content};
onClick={variableName}
It looks like you question has already been answered. Just a side note though: remember that when assigning your method with an arrow function
onClick={ () => this.method() }
a new anonymous function is created on every re-render. So if the method doesn't need any arguments, it's better to reference the method directly (without parentheses so it's not invoked).
onClick={ this.method }
The first will call the function every time render is done.
The second will do what you want - call it onClick.

Why are stateless functions not supposed to have methods?

I have read in multiple places that stateless functions in React are not supposed to have inner functions. Why is it so, though it works?
const Foo = () => {
let bar = () => {
return <span>lorem ipsum</span>
}
return <div>{bar()}</div>
}
This works. But, why is this not supposed to be done?
N.B. This answer assumes that the use of the word "method" was incorrect, and that we are actually talking about an inner function, as in the example provided in the question.
A stateless component is defined as a function which returns something that can be rendered by React:
const MyStatelessComponent = function (props) {
// do whatever you want here
return something; // something must be render-able by React
}
To (re-)render the component, React calls the function, so it makes sense to perform expensive computations in advance and save their result outside of the function.
In your toy example, the function bar is declared once per render, and only used once. Let's assume that it was slightly more complicated and pass it a single parameter:
const Foo = () => {
let bar = text => {
return <span>{text}</span>
}
return <div>{bar("lorem ipsum")}</div>
}
By moving bar outside of the component, you don't need to create the function once per render, you just call the function that already exists:
const bar = text => {
return <span>{text}</span>
}
const Foo = () => {
return <div>{bar("lorem ipsum")}</div>
}
Now your component is ever-so-slightly more efficient, since is does less work every time it is called.
Also note that bar is almost the same as a stateless component now, and could easily be turned into one by making the function take a props object rather than a single string argument.
But the bottom line is that you can do whatever you want inside the stateless component. It just is worth bearing in mind that it will happen once per (re-)render.
While still valid code, as far as react is concerned, the above example is not a stateless component.
A stateless component is basically a shortcut to the render method of a stateful component (without the same life-cycle) and should "ideally" only return data, not define methods or actually manipulate or create additional data or functionality. With a stateful component, ideally, you do not define methods within the render method so none should be added in a stateless component.
By defining a method, function, or parameter inside of a stateless component but outside of the render method, you are essentially saying that there is a possibility of manipulation within the stateless component, which defeats the purpose.
Mind you, it's still valid code...but just not "react" ideal.
The function Foo is basically the render method of the React component. Therefore, it will be called everytime the component needs to be rendered. By declaring a local function inside it, it will create a new function everytime the component re-renders, which is bad.
Either declare the function outside or implement a stateful component instead.

Categories

Resources