Invalid Hook call in React in function call - javascript

I'm quite new to react and get the following 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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Situation:
in de index.js file I have the
ReactDOM.rend(,document.getElementByID('root'))
In this file, before the document.getElementByID, I route to all the different pages using react-router-dom. One of those pages contains a form, when this form is submitted I want to do something with the results of that form.
The form has an onSubmit to a function like:
const functionName = () => {
if (formID === "XXXXXX") {
<ComponentName/>
}
}
In this function I want to check if the ID of the form matches a certain ID, before calling another Component. Until this point it works perfectly, the function is called, it checks if the ID's are matching, but then:
What is the right way to call another component?
If I use componentName() I get this Invalid Hook call because the componentName file has some useState hooks. and if I use <componentName/> nothing happens the console logs wont show up in the console etc.
Note: In the componentName file, I just want to get the information that is used in the form, before passing it on to another file. so the componentName file doesnt have to render anything.
UPDATE: To load the forms in a page, there is a button: <Button fid={id} pageName='pageName'/>
In that Button file I can use hooks without a problem, the button has an onClick like: <button className={pageName} onClick={openTF}>buttonName</button> then that openTF function leads to another page where the form is shown and handled in a popup:
const openTF = () => {
Form(props.fid);
}
From that file on, I can't use any hooks
Update2:
I found out that the only works in the return part of a Component.
So i rewrote most of the code, and only problem I now have is that in that the return part is rendered everytime the useStates are updated. is there a way to make another AFTER all the useState. the way it is now the return part is called 4 times, and I only need it one time after all the UseStates
Does anyone know what I do wrong?

Related

Assigning onclick on button that is outside of current react app (php) gives unexpected behaviour

We are builidng react components for a php app, it doesn't really have much intergration but there's a php save button that is should listen for and save onclick. But there's a catch, for some reason most of functions i'm trying to use are just not getting ran. I'm working with react-form-hook and trying to run the onsubmit() with my function inside.
handleSubmit((data) => {
handleSaveOrderProducts(data, selectedProductsRef.current);
});
To assign this function to the button i have to listen for the button clicks with id, i tried different approaches but none seems to fix the problem. Ways to listen i tried:
Using https://www.npmjs.com/package/react-detect-click-outside#features lib
const productInformationContainerRef = useDetectClickOutside({
onTriggered(event) {
// #ts-ignore
if (event.target?.id === "save-order-button") {
console.log("save order button");
handleSubmit((data) => {
handleSaveOrderProducts(data, selectedProductsRef.current);
});
}
},
});
What i get in result? The callback itself runs, but the function (handlesubmit) is not. Even if i put a console.log in callback with data there's just nothing displayed.
Different approaches from this stackoverflow thread, mostly everything related to functional react Detect click outside React component .
If you had experience in something like this or have any theoretical knowledge please comment :)strong text

Use Hook in function result in invalid hook call

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.

How to get document element when I use external script in react?

I'm using React to build my project. I made a chat button by using external script. And I'd like to disappear that button in some specific pages. I can't use a ref to access that button's element So I used document.getElementById.
But my problem is my code sometimes returns error. (I think when my code runs, chat button didn't run by external script.) How can I solve this problem?
useEffect(() => {
//access chat button element by using document.getElementById
const chatBtn = document.getElementById('ch-plugin-launcher');
//if it doesn't exist in current page, it returns.
if (!chatBtn) {
return;
}
//if a button exists, it will be hide.
chatBtn.classList.add('hide');
return () => {
chatBtn.classList.remove('hide');
};
}, []);
I think the error in return of useEffect. You return a function, which can be called at any time whenever the chat button does not exist. Add check for existing on the chat button in the useEffect return function. Other code looks well.
useEffect(() => {
// Your code
return () => {
document.getElementById('ch-plugin-launcher')?.classList?.remove('hide');
};
});
I think #0x6368656174 answwer is correct.
Just for more clarification why:
When exactly does React clean up an effect? React performs the cleanup
when the component unmounts. However, as we learned earlier, effects
run for every render and not just once. This is why React also cleans
up effects from the previous render before running the effects next
time. We’ll discuss why this helps avoid bugs and how to opt out of
this behavior in case it creates performance issues later below.
Source: https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1

When is it necessary to use `rerender` with the React Testing Library?

In times past, my colleagues and I would typically write React Testing Library (RTL) tests for the main parent components, which often have many nested child components. That testing made sense and worked well. Btw the child components in question are very much dedicated to that parent component and not of the reusable variety.
But now we're trying to write RTL tests for every single component. Today I was trying to build tests for an Alerts component, which is the parent of an Alert component and about 4 levels down from the top-level component. Here's some sample code in my test file:
function renderDom(component, store) {
return {
...render(<Provider store={store}>{component}</Provider>),
store,
};
}
let store = configureStore(_initialState);
const spy = jest.spyOn(store, 'dispatch');
const { queryByTestId, queryByText, debug } = renderDom(
<Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} />,
store
);
I then started writing the typical RTL code to get the Alerts component to do its thing. One of these was to click on a button which would trigger an ADD_ALERT action. I stepped through all of the code and the Redux reducer was apparently working correctly with a new alert, as I intended, yet back in the Alerts component, question.alerts remained null whereas in the production code it was definitely being updated properly with a new alert.
I spoke with a colleague and he said that for this type of test, I would need to artificially rerender the component like this:
rerender(<Provider store={store}><Alerts question={store.getState().pageBuilder.userForm.steps[0].tasks[0].questions[1]} /></Provider>);
I tried this and it appears to be a solution. I don't fully understand why I have to do this and thought I'd reach out to the community to see if there was a way I could avoid using rerender.
It's hard to be certain without seeing more of your code, but my typical approach with RTL is to take the fireEvent call that simulates clicking the button and wrap it in an act call. This should cause React to finish processing any events from your event, update states, rerender, etc.
Alternatively, if you know that a particular DOM change should occur as a result of firing the event, you can use waitFor. An example from the React Testing Library intro:
render(<Fetch url="/greeting" />)
fireEvent.click(screen.getByText('Load Greeting'))
await waitFor(() => screen.getByRole('alert'))

Next JS: Right way to fetch client-side only data in a Functional Component

I have a functional component. Basically, the page consists of a form - where I need to populate some existing data into the form and let the user update it.
I'm using a hook that I wrote to handle the forms. What it does is this
const [about, aboutInput] = useInput({
type: 'textarea',
name: 'About you',
placeholder: 'A few words about you',
initialValue: data && data.currentUser.about,
})
about is the value and aboutInput is the actual input element itself. I can pass an initialValue to it too.
At the beginning of the component I'm fetching the data like so:
const { data, loading, error } = useQuery(GET_CURRENT_USER_QUERY)
This only is executed on the client side and on the server side data is undefined.
Hence this code only works when I navigate to the page through a Link component from another client-side page.
It doesn't work for:
When I go the URL directly
When I navigate to this page from another SSR page(which uses getInitailProps)
I don't want to use lifecycle methods/class component(since I'm using hooks, and want to keep using the functional component.
Is there a nice way to achieve this in Next JS and keep using functional component?
You can fetch client-side only data using the useEffect Hook.
Import it first from react
import { useEffect } from 'react';
Usage in the component looks like follows
useEffect(() => {
return () => {
// clean-up functions
}
}, []);
The first argument Is a function and you can make your API calls inside this.
The second argument to the useEffect will determine when the useEffect should be triggered. If you pass an empty array [ ], then the useEffect will only be fired when the component Mounts. If you want the useEffect to fire if any props change then pass such props as a dependency to the array.
If you want GET_CURRENT_USER_QUERY from the query string you can pass the argument from the getInitailProps and read this as props in the useEffect array dependency.
I see you mentioned getInitailProps. So you probably know what it does and its caveats. If you want the data to be defined on server side, why not use getInitailProps on the page which the form is in. That way you can retrieve the data from props and pass it to your form component. It should work either directly visiting the url or visiting from your other pages.

Categories

Resources