When I declare function in useEffect, it is not declared in html - reactjs

I am implementing to change the state depending on it is hovered or not. But if I make it like the first code below, Too many re-renders. error will occur. In order to solve this problem, I used the useEffect to render only when the state changes, but an error called onHover is not defined occurs that the function declared in the user effect was not declared.
not using useEffect
import "./styles.css";
import { useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
return (
<button onMouseEnter={setIsHover(true)} onMouseLeave={setIsHover(false)}>
click
</button>
);
}
using useEffect
import "./styles.css";
import { useEffect, useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
useEffect(()=>{
const onHover = () => {
setIsHover(true)
}
},[isHover])
return (
<button onMouseEnter={onHover()} onMouseLeave={setIsHover(false)}>
click
</button>
);
}
What should I do to use the function declared in useEffect?

As you just want to setState so no need to use useEffect.
You can use without using useEffect as below.
import "./styles.css";
import { useState } from "react";
export default function App() {
const [isHover, setIsHover] = useState(false);
return (
<button onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
click
</button>
);
}

It has to do with scope. The onHover function is defined within the useEffect hook, so it goes out of scope once you're out of the hook's block. You'll have to define it directly inside the component, outside of any other block scope, and simply call it inside useEffect.
It will still result in onHover called so many times until the mouse leaves the element in question. To prevent it, you could add a condition like so:
const onHover = () => {
if (!isHover) {
setIsHover(true);
}
}

Related

React MUI - make a dialog appear on small screens - invalid hook call

I need to show a dialog only on small screens. After researching I found out that there is a hook called useMediaQuery() which returns a boolean if the browser meets a specific resolution.
I'm using "#mui/material": "^5.1.1"
This is my implementation:
import { useTheme, useMediaQuery, Dialog, DialogTitle } from '#mui/material';
import { useEffect, useState } from 'react';
const MyDialog = () => {
const theme = useTheme();
const [dialogOpen, setDialogOpen] = useState(false);
useEffect(() => {
setDialogOpen(useMediaQuery(theme.breakpoints.down('lg')));
});
return (
<Dialog open={dialogOpen}>
<DialogTitle>This is a test Dialog Title</DialogTitle>
</Dialog>
);
};
However this is giving me the 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:
It seems it doesn't allow me to use hooks instide the useEffect hook.
How can I fix it otherwise, in order to achieve the same result, which is to update the useState state true or false according to the resolution of the browser?
You cannot use hooks inside a hook call (useMediaQuery in the callback of useEffect) even though you can use hooks inside another hook definition.
Fix it by moving useMediaQuery to the top level of MyDialog component.
Then use the output value in useEffect.
import { useTheme, useMediaQuery, Dialog, DialogTitle } from "#mui/material";
import { useEffect, useState } from "react";
const MyDialog = () => {
const theme = useTheme();
const [dialogOpen, setDialogOpen] = useState(false);
const matches = useMediaQuery(theme.breakpoints.down("lg"));
useEffect(() => {
setDialogOpen(matches);
});
return (
<Dialog open={dialogOpen}>
<DialogTitle>This is a test Dialog Title</DialogTitle>
</Dialog>
);
};
Hook should be called only on top level
You can only call Hooks while React is rendering a function component:
✅ Call them at the top level in the body of a function component. ✅
Call them at the top level in the body of a custom Hook.
const theme = useTheme();
const mediaQuery = useMediaQuery(theme.breakpoints.down('lg'))
Try to do setState only on mount to avoid infinite re-renders, Since you're computing browser specification setDialogOpen can be set once.
useEffect(() => {
setDialogOpen(mediaQuery);
},[]);
Read more about your error here

React: how to fire useeffect using context variable as dependency?

I was trying to figure out how to fire a useEffect when its dependency is a useContext variable. My Context has an "update" variable, which at one point changes, but the effect isn't fired.
import { useEffect, useState, useContext } from "react"
import context from "./Context"
const Layout = () => {
const ctx = useContext(context)
const [updateState, setUpdateState] = useState(ctx.update)
useEffect(() => {
console.log("effect fired")
}, [updateState])
return (
<div>
</div>
)
}
export default Layout
I tested whether the issue was my context "update" variable not changing, but it does. I will appreciate any help with this.
Your problem is that you used useState. This in effect memoized/froze the value of updateState, to the first run of this component.
You see useState takes a single arg (default value). Which is only used on the first render of the component, to populate updateState. When ctx.update changes, useState already has a default to set updateState to, so it is ignored.
Instead, you can remove useState completely...
import { useEffect, useState, useContext } from "react"
import context from "./Context"
const Layout = () => {
const { update as updateState } = useContext(context);
// Notice I have removed the useState.
useEffect(() => {
console.log("effect fired")
}, [updateState])
return (
<div>
</div>
)
}
export default Layout

arrow function inside functional component is undefined react hooks

I'm trying to make a like button counter with react hooks. I have defined the arrow function this way, but I'm getting IncreaseLikeHandler is undefined.
import {useState} from "react";
import './App.css';
function App() {
IncreaseLikeHandler = () =>{
setLikeCounter(likeCounter+1);
};
const [likeCounter, setLikeCounter] = useState(0);
const [dislikeCounter, setDislikeCounter] = useState(0);
return (
<div className="App">
<p>test</p>
<button onClick={IncreaseLikeHandler}>like</button>
<button>dislike</button>
<div>{likeCounter}</div>
</div>
);
}
export default App;
You first have to declare it then you can use it
without declaration eslint will complain
'IncreaseLikeHandler' is not defined. (no-undef)eslint
const IncreaseLikeHandler = () => {
setLikeCounter(likeCounter + 1);
};
Inside the fanction component you must use (const, let and var) to define the functions.

React.js - passing functions between components using Context API - not working

I am trying to pass a function between two components but even though I do not have any errors, the function that I am passing wont show or to be precise it is not working. I have two files and one of them is creating a context while the other is using it (obviously). Now, they are not shown in App.js (which is rendered in index.js, usual stuff) they are in the seperate folder. I am using React Router to show one the pages (News.js).
Here are the files:
NewsContext.js
import React, { useContext, createContext, useState, useEffect } from "react";
export const newsK = React.createContext();
export const NewsContext = (props) => {
const working = () => {
console.log("it is working");
};
return <newsK.Provider value={working}>{props.children}</newsK.Provider>;
};
export default NewsContext;
News.js
import React, { useContext, useState, useEffect } from "react";
import { newsK } from "./NewsContext";
import { NewsContext } from "./NewsContext";
const News = () => {
const data = useContext(newsK);
return (
<NewsContext>
<div>
<button onClick={data}></button>
</div>
</NewsContext>
);
};
export default News;
When i pressed the button, it wont do anything. Any tips?
You're trying to use context outside the NewsContext component
The solution for this will be to create a component that will call useContext inside NewsContext.
I.e.
const WrappedButton = () => {
const data = useContext(newsK)
return <button onClick={data} />
}
And then put it inside the NewsContext:
<NewsContext>
<div>
<WrappedButton />
</div>
</NewsContext>

Have to click twice to update state (useState hook) with onClick event

I've seen more questions being post here about this, but the solutions did not work for me.
The problem is that I: have to click twice to update the state of a constant defined using the useState hook
How can I make this work only having to click once?
Please see the code on CodeSandbox, or the code below:
App.js
import React, { useState } from "react";
import "./styles.css";
import Row from "./Row";
export default function App() {
const [adults, setAdults] = useState(1);
return <Row adults={adults} setAdults={setAdults} />;
}
Row.jsx
import React, { useEffect } from "react";
export default function({ adults, setAdults }) {
useEffect(() => {
console.log(adults);
}, [adults]);
return (
<>
<button onClick={() => setAdults(adults++)}>{adults}</button>
</>
);
}
Either do:
setAdults(adults+1)
or:
setAdults(++adults)
Be careful using ++ because someObject.counter++ will mutate someObject.

Resources