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.
Related
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);
}
}
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
Trying to understand context api, and I understand props are passed down. I am trying to change state of my Context file's value to another number like 50.
Created Context File
import React, { useState, createContext } from "react";
export const PointsContext = createContext();
export const PointsProvider = (props) => {
const [points, setPoints] = useState(0);**<--WANT TO CHANGE THIS**
return <PointsContext.Provider value={points}>{props.children}</PointsContext.Provider>;
};
Wrapped Everything In Provider in App.js
import {PointsProvider } from "./PointsContext";
<PointsProvider>
<ChildComponent>
</PointsProvider>
The "ChildComponent" is Provided Context
import React, { useState, useEffect, useContext } from "react";
import { PointsContext } from "../PointsContext";
const value = useContext(PointsContext);
return(
<Button title="ChangeNumber" onPress={() => Change value to 50 }/>
)
I figured it out, instead of importing
const value = useContext(PointsContext);
import this, which gives access to the setState in the context file. As long as you import this on to any screen you will have access to that useState to change stuff.
const [points, setPoints] = useContext(PointsContext);
the rest might go something like this!
<Button title="ChangeNumber" onPress={() => setPoints(50)}/>
<Text>{points}</Text>
This tutorial helped alot thanks devEd, he is one of my favs!
DevEd Youtube React State
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>
The state variable likes just continues to increment. I thought I have my onSelect function alternating the boolean value of liked, but apparently not.
Here's the error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls
setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested
updates to prevent infinite loops.
Here's App.js:
import React, { useState } from 'react';
import './App.css';
import LikeButton from './components/LikeButton';
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) { //This function is happening, but the else block
// never fires.
if (liked) {
updateLikes(likes + 1);
} else { updateLikes(likes - 1)
};
updateLiked(!liked);
}
}
/>
// onClick function here, or in LikeButton.js?
);
}
export default App;
And here's LikeButton.js:
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div key={secret} liked={liked} onClick={onSelect(liked)}>Like Button</div>
<p>{numLikes}</p>
</>
);
}
Issue
You invoke your onClick callback immediately, which updates state in the parent.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={onSelect(liked)} // invoked right away
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Solution
Make it an anonymous inner function.
import React from 'react';
import { FaThumbsUp } from 'react-icons/fa';
export default function LikeButton({secret, liked, numLikes, onSelect}) {
return (
<>
<FaThumbsUp />
<div
key={secret}
liked={liked}
onClick={() => onSelect(liked)} // invoked when clicked
>
Like Button
</div>
<p>{numLikes}</p>
</>
);
}
Suggestion
You should also really use functional state updates when the next state values depend on current state values, i.e. when incrementing/decrementing counts or toggling a boolean. I also suggest to move the updateLikes into an useEffect hook to react to liked being toggled. This may make the logic about incrementing/decrementing a little clearer.
function App() {
const [likes, updateLikes] = useState(23);
const [liked, updateLiked] = useState(false);
useEffect(() => {
updateLikes(likes => likes + (liked ? 1 : -1));
}, [liked]);
return (
<LikeButton
secret='like-button'
numLikes={likes}
liked={liked}
// status={liked}
onSelect={function clickLike(liked) {
updateLiked(liked => !liked);
}}
/>
);
}