Use Hook in function result in invalid hook call - javascript

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.

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.

React Hooks - What's happening under the hood?

I've been trying out React Hooks and they do seem to simplify things like storing state. However, they seem to do a lot of things by magic and I can't find a good article about how they actually work.
The first thing that seems to be magic is how calling a function like useState() causes a re-render of your functional component each time you call the setXXX method it returns?
How does something like useEffect() fake a componentDidMount when functional components don't even have the ability to run code on Mount/Unmount?
How does useContext() actually get access to the context and how does it even know which component is calling it?
And that doesn't even begin to cover all of the 3rd party hooks that are already springing up like useDataLoader which allows you to use the following...
const { data, error, loading, retry } = useDataLoader(getData, id)
How do data, error, loading and retry re-render your component when they change?
Sorry, lots of questions but I guess most of them can be summed up in one question, which is:
How does the function behind the hook actually get access to the functional/stateless component that is calling it so that it can remember things between re-renders and initiate a re-render with new data?
React hook makes use of hidden state of a component, it's stored inside a fiber, a fiber is an entity that corresponds to component instance (in a broader sense, because functional components don't create instances as class components).
It's React renderer that gives a hook the access to respective context, state, etc. and incidentally, it's React renderer that calls component function. So it can associate component instance with hook functions that are called inside of component function.
This snippet explains how it works:
let currentlyRenderedCompInstance;
const compStates = new Map(); // maps component instances to their states
const compInstances = new Map(); // maps component functions to instances
function useState(initialState) {
if (!compStates.has(currentlyRenderedCompInstance))
compStates.set(currentlyRenderedCompInstance, initialState);
return [
compStates.get(currentlyRenderedCompInstance) // state
val => compStates.set(currentlyRenderedCompInstance, val) // state setter
];
}
function render(comp, props) {
const compInstanceToken = Symbol('Renderer token for ' + comp.name);
if (!compInstances.has(comp))
compInstances.set(comp, new Set());
compInstances.get(comp).add(compInstanceToken);
currentlyRenderedCompInstance = compInstanceToken;
return {
instance: compInstanceToken,
children: comp(props)
};
}
Similarly to how useState can access currently rendered component instance token through currentlyRenderedCompInstance, other built-in hooks can do this as well and maintain state for this component instance.
Dan Abramov created a blog post just a couple days ago that covers this:
https://overreacted.io/how-does-setstate-know-what-to-do/
The second half specifically goes into details regarding hooks like useState.
For those interested in a deep dive into some of the implementation details, I have a related answer here: How do react hooks determine the component that they are for?
I would recommend reading https://eliav2.github.io/how-react-hooks-work/
It includes detailed explanations about what is going on when using react hooks and demonstrate it with many interactive examples.
Note - the article does not explain in technical terms how React schedule calls for later phases, but rather demonstrates what are the rules that react uses to schedule calls for later phases.
The URL in another answer given by Eliav Louski is so far the best React explaination I have come across. This page should replace React's official tutorial as it removes all the magic from hooks and friends.

render React component from a string

I have some React code in the string, for example:
const component = `
function App() {
return (
<div>
test
</div>
);
}
`;
And I want to be able to render that component from within browser, something like:
import React, { Component } from 'react';
import { render } from 'react-dom';
import * as babel from 'babel-standalone';
const babelCode = babel.transform(component, { presets: ['react', 'es2015'] }).code;
render(eval(babelCode), document.getElementById('WorkFlow'));
This particular example doesn't work but it shows what I'm looking for, any help appreciated!
Thanks!
Babel produces the code with "use strict" and eval() doesn't work with it well. First, we should remove that line manually.
const code = babelCode.replace('"use strict";', "").trim();
Ideally, after this following lines should work.
eval(code);
render(<App/>, document.getElementById('WorkFlow'));
Note that you don't need to put eval() inside render. It doesn't return your App function or anything. Instead, it will add App to context and we can use it after eval() statement.
But usually, React app has a compile step with webpack or similar tool and will complain about undefined App.
As a workaround, we can wrap our component with a Function which returns our component itself. Now we can call this function to get our component. But the context of wrapping function doesn't have React variable. So we have to pass it manually as a parameter. If you are going to use any other variable from the current context, you will have to pass those as well.
const code = babelCode.replace('"use strict";', "").trim();
const func = new Function("React", `return ${code}`);
const App = func(React)
render(<App/>, document.getElementById('WorkFlow'));
Hope this helps!
React will allow you to render either a Component or an Element. You can think of an Element as a raw HTML code in JSX, while a Component is a prototype, which inherits from React.Component. In your code you are trying to render a result of evaluating the babel transpiled code, which will fail (I'm not sure what it is, but it's probably undefined or null). If you want to make it work, first evaluate the code and then invoke the function to pass the Element code to the render function:
eval(babelCode); // now the App function has been defined and you can use it in the code
render(App(), document.getElementById('WorkFlow'));
// ^^ here the App function is being invoked
Old answer (I thought you were trying to pass the component as a file not and not as a variable to transpiler):
babel will never transpile strings, so this is not going to work out for you. You can however consider using a raw JS code instead of JSX as your string content. More about it you can read here: https://facebook.github.io/react/docs/react-without-jsx.html

Using higher order components with Redux containers

First, some context.
I'm using Redux to manage authentication state of my app and have Auth as a Redux container (or smart component).
I've created a wrapper (a higher-order component) that takes Auth and returns it:
export default function AuthWrapper(WrappedComponent) {
class Auth extends Component {
... <Auth stuff here> ...
}
return connect(mapStateToProps, mapDispatchToProps)(Auth);
}
It seems to me that in order to use the wrapper, I just need to invoke it with a component I want to have behind my auth. For example, let's say I'm authenticating a component called UserPage with the wrapper, à la:
const AuthenticatedUserPage = AuthWappper(UserPage)
However, when I use the wrapper like this, React isn't happy with me. I get the following error:
Warning: AuthenticatedApp(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
My best guess is that it doesn't like the connect-ified component that Redux will create when I return it from AuthWrapper... which leads me to my question:
Does React support higher-order components when those components create Redux containers? And if so, why would React be throwing this error?
Here's my two cents. I think the error is occurring elsewhere.
According to this simplified version of the connect function in react-redux, the connect function is simply returning another react component. So in your case, you're returning a component, wrapped inside another component, which is still valid. A container is basically a component.
Read https://gist.github.com/gaearon/1d19088790e70ac32ea636c025ba424e for a better understanding of the connect function.
I also tried the following in my own application and it worked.
import Layout from '../components/Layout'
//Do some other imports and stuff
function wrapper(Layout) {
return connect(null, mapDispatchToProps)(Layout);
}
export default wrapper()
Like the error states, you might just simply be returning an invalid component somewhere in your app. Your app might be throwing the error because you're not wrapping a return call in parentheses on your render method.

Categories

Resources