React useContext cannot make simple counter work - reactjs

I was trying to make a simple example illustrating how useContext work.
I have started with this sandbox code:
https://codesandbox.io/s/react-typescript-usecontext-lama-1v7wd
The issue is that the component Counter does not update and rerender when I click the button.
My index
import React, { useContext } from 'react'
import { MyContextProvider, MyContext } from './Context'
import { render } from 'react-dom'
const MyCounter = () => {
const context = useContext(MyContext)
const { counter } = context
const { setCounter } = context
return (
<div>
Valeur du compteur : {counter}
<br />
<button onClick={() => setCounter(counter - 1)} type="button">
-1
</button>
<button onClick={() => setCounter(counter + 1)} type="button">
+1
</button>
<br />
<button onClick={() => setCounter(0)} type="button">
RàZ
</button>
</div>
)
}
const rootElement = document.getElementById('root')
render(
<MyContextProvider>
<MyCounter />
</MyContextProvider>,
rootElement
)
My context:
type MyContextProps = {
counter: number
setCounter: Dispatch<SetStateAction<number>>
}
const MyContext = createContext({} as MyContextProps)
const MyContextProvider: React.FunctionComponent = (props) => {
const [counter, setCounter] = useState<number>(0)
return (
<MyContext.Provider
value={{
counter: 0,
setCounter: setCounter,
}}
>
{props.children}
</MyContext.Provider>
)
}
export { MyContext, MyContextProvider }
It's got to be something elementary, but I just can't see it.

Just a small error.
in your context, you have set your counter to be always zero. Change this to be counter state and your problem should be resolved.
const MyContextProvider: React.FunctionComponent = (props) => {
const [counter, setCounter] = useState<number>(0)
return (
<MyContext.Provider
value={{
counter: counter, //<-- HERE.
setCounter: setCounter,
}}
>
{props.children}
</MyContext.Provider>
)
}
As a suggestion, consider using function to get the latest state of your value when setting the next value using the setCounter function, if the next value is dependent on the previous value.
The mutation function from useState can also accept a callback function, which provides the latest current state and should return the next state value based on the previous value. This is especially helpful if setting state is in an async operation preventing stale closures.
(prevValue) => nextValue
<button onClick={() => setCounter(prevValue => prevValue - 1)} type="button">
and
<button onClick={() => setCounter(prevValue => prevValue + 1)} type="button">

Related

How are the following two statements different in React?

I was looking at the react docs today (https://beta.reactjs.org/learn/preserving-and-resetting-state), and I'm a bit confused on something.
In the docs, it says that you can force the state to reset by rendering a component in different positions, I get that.
When isPlayerA state changes, this one does not reset the state for Counter:
// App.js
import { useState } from 'react';
export default function Scoreboard() {
const [isPlayerA, setIsPlayerA] = useState(true);
return (
<div>
{isPlayerA ? (
<Counter person="Taylor" />
) : (
<Counter person="Sarah" />
)}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
Next player!
</button>
</div>
);
}
function Counter({ person }) {
const [score, setScore] = useState(0);
const [hover, setHover] = useState(false);
let className = 'counter';
if (hover) {
className += ' hover';
}
return (
<div
className={className}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
>
<h1>{person}'s score: {score}</h1>
<button onClick={() => setScore(score + 1)}>
Add one
</button>
</div>
);
}
When isPlayerA state changes, this one DOES reset the state for Counter:
return (
<div>
{isPlayerA &&
<Counter person="Taylor" />
}
{!isPlayerA &&
<Counter person="Sarah" />
}
<button onClick={() => {
setIsPlayerA(!isPlayerA);
}}>
Next player!
</button>
</div>
);
In both situations, it seems to be that Counter is getting rendered in the same position, so why is the second one resetting the state? Are there two Counter components in the UI tree on the second one where state does get reset?
Thanks for any help!

How to persist counter value when the state changes?

In this component when I change the state, it reassigns counter=1 which I do not want. I just want that it assigns counter=1 only once and then does not assign it again during re-render.
And, I do not want to use useState for counter to solve this issue.
function App() {
const [value, setValue] = useState(100);
let counter = 1;
return (
<div>
<button
onClick={() => {
setValue(value - 1);
counter++;
}}
>
click me
</button>
</div>
);
}
export default App;
You can use useRef:
useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useRef doesn’t notify you when its content changes. Mutating the
.current property doesn’t cause a re-render.
function App() {
const [value, setValue] = useState(100)
const counter = useRef(1)
return (
<div>
<button
onClick={() => {
setValue(value - 1)
counter.current++
}}
>
click me
</button>
</div>
)
}
You can persist the counter with localStorage:
import { useState } from "react";
function App() {
const [value, setValue] = useState(100);
let counter = localStorage.getItem('counter') || 1;
return (
<div>
<button
onClick={() => {
setValue(value - 1);
localStorage.setItem('counter', ++counter);
}}
>
click me
</button>
</div>
);
}
export default App;

import and export may only appear at the top level - reactjs

Good day Everyone,
I'm trying to run a query that increases a number whenever a button is clicked. I'm using a course from udemy is kinda old and our setup/installation is different. Please look at my code down below.
let count = 0;
const addOne = () => {
count++;
rendertheCounterApp();
};
const minus =() => {
console.log('Minus',);
}
const Reset = () => {
console.log ('Reset');
};
const rendertheCounterApp = () => {
const App = () => {
return (
<div className='App'>
<h1>Toggler {count} </h1>
<button onClick ={addOne}> +1 </button>
<button onClick={minus}>-1</button>
<button onClick ={Reset}>Rest</button>
</div>
);
};
export default App;
};
rendertheCounterApp();
is there any way I can modify that code to make the number increase whenever I click on the button?
Thanks in advance.
You need to have state to store the count and you can increase or decrease the count without creating any special functions. Try something like:
import React, { useState } from 'react';
const RenderTheCounterApp = () => {
const [count, setCount] = React.useState(0);
return (
<div>
<h1>count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Add</button>
<button onClick={() => setCount(count - 1)}>Subtract</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
};

How to use Switch case in React js functional component inside return efficiently by reusing?

I want to use Switch case in React js functional component.
I want an efficient way of using it.
I have this code snippet:
import React, { useState } from 'react';
import FormUserDetails from './FormUserDetails';
function UserForm() {
const [step, setStep] = useState(1);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const nextStep = () => {
setStep(prevState => prevState + 1)
}
const previousStep = () => {
setStep(prevState => prevState - 1)
}
switch (step) {
case 1:
return (
<div>
<FormUserDetails
/>
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
</div>
)
case 2:
return (
<div>
<h1>{step}</h1>
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
</div>
)
default:
return (
<div>
<h1>Final</h1>
</div>
)
}
}
export default UserForm
This code is working fine.
However, in the return
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
I am repeating this code. I just want to know an efficient code structure to reuse these lines with every switch case.
You can apply a little more code refactoring, factoring the buttons into a reusable component.
const StepButtons = () => (
<Fragment>
<button onClick={nextStep}>next</button>
<button onClick={previousStep}>previous</button>
</Fragment>
);
Result
import React, { Fragment, useState } from 'react';
import FormUserDetails from './FormUserDetails';
function UserForm() {
const [step, setStep] = useState(1);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const nextStep = () => {
setStep(prevState => prevState + 1);
};
const previousStep = () => {
setStep(prevState => prevState - 1);
};
const StepButtons = () => (
<Fragment>
<button onClick={nextStep}>next</button>
<button onClick={previousStep}>previous</button>
</Fragment>
);
switch (step) {
case 1:
return (
<div>
<FormUserDetails />
<StepButtons />
</div>
);
case 2:
return (
<div>
<h1>{step}</h1>
<StepButtons />
</div>
);
default:
return (
<div>
<h1>Final</h1>
</div>
);
}
}
export default UserForm
You can create a new component in a different file (if you want to distinguish this component of course)
import React from 'react'
export default function NavigationButtons(props) {
return (
<>
<button onClick={props.nextStep}>
next
</button>
<button onClick={props.previousStep}>
previous
</button>
</>
)}
and then use it like this
<NavigationButtons nextStep={nextStep} previousStep={previousStep}/>

too many re renders with add count button

I am new to React. I have a simple page with buttons which increase the count by 1 or decrease by 1. The solution shows the right way which is by using inline functions for the minus button.However when I changed the plus button to use a regular function, I am getting too many re render.
import React, { useState } from 'react'
import randomColor from 'randomcolor'
export default function Playground() {
const [count, setCount] = useState(0)
const add = (n) =>{
setCount(n+1);
};
return (
<div>
{count}
<button onClick={() => add(count)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
<button onClick={add(count)}>+</button> immediately triggers add function when the component render.
Try this:
import React, { useState } from 'react'
import randomColor from 'randomcolor'
export default function Playground() {
const [count, setCount] = useState(0)
const add = () =>{
setCount(count+1);
};
return (
<div>
{count}
<button onClick={() => add()}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
You can use regular function or arrow function. But in your case, you were calling the add function every time it re-renders. Consider the difference in the following vanilla JavaScript code snippets:
function add() {
console.log("Add");
}
document.getElementById("Button").addEventListener("click", add);
vs
function add() {
console.log("Add");
}
document.getElementById("Button").addEventListener("click", add()); //<--
The first one is the way to go.
In the React world, the following implementations are all valid:
function ButtonWithArrowFunction() {
const handleClick = () => {
console.log("Clicked");
};
return <button onClick={handleClick}>Press Me</button>;
}
function ButtonWithInlineArrowFunction() {
return (
<button
onClick={() => {
console.log("Clicked");
}}
>
Press Me
</button>
);
}
function ButtonWithRegularFunction() {
function handleClick() {
console.log("Clicked");
}
return <button onClick={handleClick}>Press Me</button>;
}
function ButtonWithInlineRegularFunction() {
return (
<button
onClick={function handleClick() {
console.log("Clicked");
}}
>
Press Me
</button>
);
}
function ButtonWithHigherOrderFunction() {
function createHandleClickFunction() {
return function handleClick() {
console.log("Clicked");
};
}
return <button onClick={createHandleClickFunction()}>Press Me</button>;
}
you can not pass add() because it will execute the funcition and will trigger an infinite cycle. Just pass the reference add of the method, so it will execute only on the click event
import React, { useState} from 'react'
const App = () => {
const [count, setCount] = useState(0);
const add = (n) =>{
setCount(count+1);
}
return (
<div>
{count}
<button onClick={add}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
</div>
)
}
export default App;

Resources