The function of my code runs twice instead of once? - javascript

`
import './App.css';
import ArrayState from './components/ArrayState';
import FetchApi from './components/FetchAPI/FetchApi';
import Login from './components/Login';
import Useeffect2 from './components/Useeffect2';
import UseEffects from './components/UseEffects';
import React,{useEffect} from 'react'
function App() {
const getUsers = async() => {
console.log("function")
}
useEffect(() => {
console.log("use")
getUsers();
});
return (
// <ArrayState/>
// <Login/>
// <UseEffects/>
// <Useeffect2/>
<FetchApi/>
);
}
export default App;
the function "getUsers" is called once in "UseState" but its runs 2 times. i want to run function once. i have define body of function into useEffect?

add empty array to the useEffect, so that it only make calls on page first load
useEffect(() => {
const getUsers = async() => {
console.log("function")
}
console.log("use")
getUsers();
}, []);

You can pass an empty array as the second argument to the useEffect hook :
useEffect(() => {
// Side Effect
}, []);
In this case, the side effect runs only once after the initial render of the component.

Already answered
You need to provide an dependency array
useEffect(() => {
}, []) // dependency array with props, variable that trigger the side effect
even this will run twice in development due to stritct mode, which helps is catching bugs related to cleanup
but if you want to disable it (not recommended)
// generally in index.js
ReactDOM.render(
<React.StrictMode> // remove this to disable stritct mode
<App />
</React.StrictMode>,
document.getElementById('root')

If you're using React 18 and your app is wrapped in the <StrictMode> tag, then this is expected behavior added on purpose in the hopes to help devs catch bugs relevant to the lifecycle of their components, such as abusing/misusing the useEffect hook.
What the new StrictMode actually does is unmount and then remount every component once it gets rendered.
Resulting in an initial lifecycle that looks like this:
* React mounts the component.
* Layout effects are created.
* Effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effects are created.
* Effects are created.
Note that it only behaves this way in dev environment, and not in the production build.
ref: https://reactjs.org/blog/2022/03/29/react-v18.html#new-strict-mode-behaviors
P.S. you should also add the empty dependency array as mentioned in other comments, otherwise it will also run every time the component re-renders.

Related

running hook inside _app.tsx Next.js

I am having issues with rendering a hook that I use to determine if my components are inside the screens view so that I can animate them. I run this hook in _app.tsx but the function does not run when routing to another page. If I put a console.log() in my _app.tsx it always runs but not mu custom hook.
If I refresh the page my hook runs as it should but I have to manually refresh the page in order for it to actually run and animate my animations.
What I have tried:
Is to run my hook in a component that I use on all of my pages it works but it takes a split second for it to load and the animations does not run smoothly. When my custom hook is in _app.tsx it does actually run smoothly when I refresh the page manually. That's why I feel like this is the place to have my hook or am I wrong? Maybe this isn't the most optimal way to do this.
The final goal is to have all may animations run smoothly. I want them to run when the component is in View and I want the animations to run again when I navigate to another page.
My _app.tsx looks like this:
import type { AppProps } from 'next/app'
import { ThemeProvider } from 'styled-components'
import GlobalStyle from '../components/GlobalStyles'
import { useInView } from '../hooks/useInView'
import { theme } from '../theme/theme'
export default function App({ Component, pageProps }: AppProps) {
useInView()
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Component {...pageProps} />
</ThemeProvider>
)
}
My custom hook (useInView()) looks like this:
import { useEffect } from 'react'
export const useInView = () => {
useEffect(() => {
function inView() {
const observer = new IntersectionObserver(
(intersections) => {
intersections.forEach(({ target, isIntersecting }) => {
if (isIntersecting) {
target.classList.add('in-view')
} else {
target.classList.remove('in-view')
}
})
},
{
threshold: 0,
}
)
document.querySelectorAll('.scroll-spy').forEach((element) => {
observer.observe(element)
})
}
inView()
}, [])
}
When you need to tell a hook to re-render when the path changes, implement a useEffect with the pathname as a dependency. Implementation is slightly different depending on if you use Next.js or React.
next.js
Take a look at the usePathname hook. Simply applied as such:
import { usePathname } from 'next/navigation';
Then inside your component you have access to the pathname:
const pathname = usePathname();
Then check when it changes before doing something:
useEffect(() => {
inView();
}, [pathname])
react.js
Take a look at the useLocation hook. It provides the pathname property.
import useLocation from 'react-router-dom'
Then inside your bring the location in:
const location = useLocation();
Then check when it changes before doing something:
useEffect(() => {
inView();
}, [location.pathname])
You'll need to slightly re-adjust where you define your inView() function but this is essentially how you do something when the view changes.
I also created a working sandbox that demonstrates IntersectionObserver observer and how to implement it using React components in case you're struggling with that. It shows an alert when the green box's visibility changes.

I have this React Invalid Hook Call Error that I'm getting and I wanted to see if I was breaking the rules of hooks or is it something else?

I'm getting the error below.
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
See (link I couldn't add) for tips about how to debug and fix this problem.
You might have mismatching versions of React and the renderer (such as React DOM)
Here is my code. Is it breaking rules of hooks or is the issue something else?
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useData } from 'useData';
export const checkReference = ({
refId,
}) => {
const data = useData(); //useContext hook
let refData = {};
if (refId) refData = data.getReference(refId);
useEffect(() => {
console.log('INITIAL LOGIC');
if (refData.parameter){
console.log('SECONDARY LOGIC', refData);
}
}, [])
checkReference.propTypes = {
refId: PropTypes.string,
}
checkReference.defaultProps = {
refId: null,
}
}
I am calling it from another file using
checkReference('page-name');
I've got the same issue in my Next.js app. It was a cache related issue. you can try these steps.
Delete node_modules and .next folders
Run npm install or yarn
Start your project again
React hooks are intended to be used inside Functional components like:
export const CustomComponent = () => {}
export function CustomComponent() {}
They cannot be used in normal function because it won't exist inside the context of a React component. If you want to use things like useState or useEffect inside functions defined outside a component, you have to create a custom hook. That is, create a function with the use prefix (e.g. useCheckReference) and then use it like:
export const MyComponent = () => {
const reference = useCheckReference()
}
In that way React knows that that function is presumably gonna be called inside a component and the use of hooks is reliable, also is going to make some optimizations related to hooks and components life cycle.

This custom async hook is not working as expected

I have this custom hook to get the current user from firebase:
import React, { Component, useEffect, useState } from 'react';
import { auth } from "../firebase/auth-service"
const useFirebaseAuthentication = (firebase) => {
const [authUser, setAuthUser] = useState(null);
try {
auth.onAuthStateChanged(async user => {
if (user) {
setAuthUser(user)
} else {
setAuthUser(null);
}
})
} catch (error) {
throw error
}
return authUser
}
export default useFirebaseAuthentication;
When I print on the screen the current user from this custom hook - I get the result as expected.
When I use the hook and try to get the user - I get null.
Can someone point out my mistake?
I don't think that useState here is appropriate, don't you get any console warnings? A hook is just a js function as any other, it's not a React component!
Try to use a local variable instead...
edit
useState is a hook, therefore you should be getting this warning:
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 See for tips about how to debug and fix this problem.
It's exactly what's a problem here: you use a hook NOT inside the body of a react functional component, you use it in an ordinary js function.

React Hook "useState" cannot be called inside a callback. Using useMediaQuery responsive JS media query

I'm trying to use responsive javascript media queries using useMediaQuery however I can't get it to work, I get: -
Error message:
"useState" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
Playground
https://stackblitz.com/edit/react-ts-5vseqr?file=media-query.ts
I think it's erroring on line 4 of media-query.ts
import { useState, useEffect } from 'react'
const useMediaQuery = (query: string) => {
const [match, setMatch] = useState(false)
useEffect(() => {
const updateMatch = () => setMatch(window.matchMedia(query).matches)
updateMatch()
window.matchMedia(query).addEventListener('change', updateMatch)
return () => {
window.matchMedia(query).removeEventListener('change', updateMatch)
}
}, [query])
return match
}
export default useMediaQuery
What you've done here is writing a custom hook(useMediaQuery). You've done that properly so no issues there. Above code snipped is fine.
The problem is in the index.tsx file when you try to use the above custom hook that you've written. As the error suggests your custom hook is called outside the react component there in line 7 of index.tsx.
You have to move the useMediaQuery call inside the App component. Also currently your App component is a class component which you have to convert to a functional component to use hooks inside it.
here's the adjusted code:
https://stackblitz.com/edit/react-ts-m6rwpd?file=index.tsx

How can I trigger a state change in a unit test with React DOM?

I'm using the React Test Utilities to unit test some of my code. I call renderIntoDocument to render a custom component and then use findDOMNode to test out what got rendered. The trouble I'm running into is that I'm not sure how to update the state and effectively trigger a re-render within the context of a unit test.
Here's some sample code -- pay attention to the code comment:
import React from 'react';
import ReactDOM from 'react-dom';
import TestUtils from 'react-dom/test-utils';
import MyCustomComponent from '../../app/components/MyCustomComponent';
describe('My Test Suite', () => {
let component, node;
test('verify state change', () => {
const items = [{'value': '1'}];
component = TestUtils.renderIntoDocument(
<MyCustomComponent items={items} />
);
node = ReactDOM.findDOMNode(component);
expect(node.querySelector('input[type=text]').value).toEqual('1');
component.state.items = [{'value': '2'}];
// do something here to trigger a re-render?
expect(node.querySelector('input[type=text]').value).toEqual('2');
});
});
Unfortunately it seems simply changing the state variable doesn't do anything. And I can't call component.componentWillReceiveProps() because that doesn't seem to be defined.
Please note that I do want the same component to call its render function rather than replacing it with, effectively, a brand new component. The reason is because I found a bug where the component was rendering things based on this.props instead of this.state, and I want a test to show that it's always using data from the state and not from the initial values.
Enzyme from AirBnb has some great utilities for this. You'll need to install the dependencies but it's simple enough to get it configured. Then, you can simply call Enzyme's setState method on your component instance. An important note – your "component instance" in this case is a shallow rendered component. Your code would look something like this:
import React from 'react';
import MyCustomComponent from '../../app/components/MyCustomComponent';
import { shallow, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// configure your adapter
configure({ adapter: new Adapter() });
describe('My Test Suite', () => {
test('verify state change', () => {
const items = [{'value': '1'}];
const wrapper = shallow(<MyCustomComponent items={items} />);
// find your shallow rendered component and check its value
expect(wrapper.find('input[type="text"]').value).toEqual('1');
// set the state of the component
wrapper.setState({ items: [{'value': '2'}] });
// state should be updated, make sure its value was properly set
expect(wrapper.find('input[type="text"]').value).toEqual('2');
});
});
All of this assumes that you are using state in your component properly. In your case, items appears to be passed in as a prop. If you are setting state by just copying props, you may want to rethink your strategy. In any case, this approach should be identical to how state updates in React work – you're operating on the same component instance without unmounting and remounting the component. Hope this helps.

Categories