export const App2 =()=>{
const [counter,setCounter]=useState(0)
setTimeout(()=>setCounter(counter+1),1000)
return(
<div> {counter}
<button onClick={()=>{setCounter(counter+1)}}> Plus</button>
</div>
)
}
I recorded a video and uploaded it to youtube. I have no idea why this instability happens and switching tabs made it normal again. Anyone to enlighten? I just made a slight modification to this code
setTimeout causes an effect, so we should put it inside a useEffect() hook. Also, since setCounter depends on the previous counter state, we can pass an updater function to setCounter which receives the previous state value.
import React, { useCallback, useEffect } from "react";
export const App2 = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => {
setCounter((counter) => counter + 1);
}, []);
useEffect(() => {
const id = setTimeout(increment, 1000);
return () => clearTimeout(id);
}, [increment]);
return (
<div>
{" "}
{counter}
<button onClick={increment}> Plus</button>
</div>
);
};
Related
When I click the increment button I expect the value displayed to go up but it stays the same
`
import * as React from 'react';
const MyComponent = () => {
var count = 0;
return (
<div>
<h1>Hello World</h1>
<button onClick={() => count++}>{count}</button>
</div>
);
};
`
You're not going to force a re render so your updated variable won't show.
import React, {useState} from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello World</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div>
);
};
In React you have to use state for rendering or updating.
This is example of increasing and decreasing counter with useState hook.
import React, { useState } from 'react';
export function App(props) {
const [count, setCount] = useState(0);
const handleIncrease = () => {
setCount(count + 1);
};
const handleDecrease = () => {
setCount(count === 0 ? 0 : count - 1);
};
return (
<div className='App'>
<div>
<h1>Hello World</h1>
<button onClick={handleIncrease}>Increase</button>
<button onClick={handleDecrease}>Decrease</button>
<h3>Count is: {count}</h3>
</div>
</div>
);
}
You need to use useState hook so the component knows when to rerender and display new data.
I also recommend using useCallback hook to create memoized functions so you prevent unnecessary rerenders of your component (I know in this example it's an overkill but it's still good to know that).
You shouldn't, if possible, return arrow functions on your handlers like onClick - this will also cause the your component to not create new instances of your functions on each render, hence better performance (again not really relevant in this super simple case but a good thing to know nevertheless).
Here are some docs that you can read, these are a really good place to get started with React.
Here's also the code:
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleCountIncrease = useCallback(() => {
setCount((c) => c + 1);
}, [setCount]);
return (
<div>
<h1>Hello World</h1>
<button onClick={handleCountIncrease}>{count}</button>
</div>
)
}
I see here you are using Functional component, so have you tried using React hooks? useState() for example:
import * as React from 'react';
const MyComponent = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>Hello World</h1>
<button onClick={() => setCount(count + 1)}>{count}</button>
</div> ); };
Try this maybe?
I've created a basic React component as a sandbox. I added a doSomething() function to the component which simply displays an alert on useEffect(). However, the alert is getting called in an infinite loop. So obviously, there's something wrong with the design. Any idea what the design issue is with this component which is causing the infinite loop? What steps would you recommend to redesign this component in order to fix that issue?
import {Repository} from '../data/Repository';
import { useEffect , useState} from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { decrement, increment } from '../features/counter/counterSlice';
import { setIsValid } from '../features/userManagement/userManagementSlice';
export const UserManagement = () => {
const [userTestData, setUserTestData] = useState([]);
const [userCount, setUserCount] = useState(0);
const count = useSelector((state) => state.counter.value)
const isValid2 = useSelector((state) => state.userManagement.isValid2)
const dispatch = useDispatch()
const doSomething = () => {
alert('about to do something');
}
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
})
useEffect(() => {
setUserCount(userTestData.length);
}, [userTestData])
//debugger;
return(
<>
<div>
{
userTestData.map((x) => {
return <div key={x.Id}>{x.FirstName}</div>
})
}
</div>
<div>there are {userCount} users</div>
<div>
{/* PARENT INPUT VALIDATION */}
<div style={{height:100}}>
<button onClick={() => dispatch(setIsValid())}>
TOGGLE VALIDATION
</button>
{isValid2 ? 'true' : 'false'}
</div>
{/* COUNTER */}
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<span>{count}</span>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
</>
)
}
The issue here.
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
})
You didn't add array of dependences to useEffect, so it's callback will be excuted on each render of the component, and as you set a State by setUserTestData, so the component will still re-render in infinite loop.
To fix.
useEffect(() => {
doSomething();
setUserTestData(Repository.getUserTestData());
}, [])
This question already has answers here:
Make React useEffect hook not run on initial render
(16 answers)
Closed 9 months ago.
I want to prevent calling my useEffect on the first mount.
how can I do this?
I want the best practice.
I don't want to use if condition
import { useState, useEffect } from "react";
const Counter = () => {
const [count, setCount] = useState(5);
useEffect(() => {
console.log(count);
}, [count]);
return (
<div>
<h1> Counter </h1>
<div> {count} </div>
<button onClick={() => setCount(count + 1)}> click to increase </button>
</div>
);
};
export default Counter;
I found it.
It should be handled by useLayoutEffect and useRef
import { useState, useEffect, useLayoutEffect, useRef } from "react";
const Counter = () => {
const [count, setCount] = useState(5);
const firstUpdate = useRef(true);
useLayoutEffect(() => {
if (firstUpdate.current) {
firstUpdate.current = false;
return;
}
console.log(count);
});
return (
<div>
<h1> Counter </h1>
<div> {count} </div>
<button onClick={() => setCount(count + 1)}> click to increase </button>
</div>
);
};
export default Counter;
Is it possible to update one state in React and in the same render, can we use the updated value to update another state?
import "./styles.css";
import {useState} from 'react';
export default function App() {
const [num, setNum] = useState(0)
const [txt, setTxt] = useState(0)
handleClick= () =>{
setNum(num+1)
setTxt(num+1)
}
return (
<div className="App">
<input type="submit" value="button" onClick={handleClick} />
<h1>{num}</h1>
<h1>{txt}</h1>
</div>
);
}
In the above example, both num and txt are initially set to 0.
Clicking the button for the first time would increase both num and txt to 1 and it would update both to 2 when clicked again and so on.
Is there way to get the num updated to 1 as soon as setNum(num+1) is called, so that it can update it from 0 to 1 and so while calling setTxt(num+1), it can update it directly to 2?
Yes, you can do that using the useEffect hook with the num as a dependency.
Here's a CodeSandbox:
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [num, setNum] = useState(0);
const [txt, setTxt] = useState(0);
const handleClick = () => {
setNum(num + 1);
};
useEffect(() => {
if (num > 0) {
setTxt(num + 1);
}
}, [num]);
return (
<div className="App">
<input type="submit" value="button" onClick={handleClick} />
<h1>{num}</h1>
<h1>{txt}</h1>
</div>
);
}
If the actual state is just a number and not something complex like an array/object then you can simply do this:
const handleClick = () => {
setNum(num + 1);
setTxt(num + 2);
};
import React, { useState, useEffect } from 'react';
const Test = () => {
const [ count, setCount ] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count up!</button>
<ChildComponent />
</div>
)
}
const ChildComponent = () => {
useEffect(() => {
console.log('render!');
return () => console.log('unmounted...');
});
return (
<div>children</div>
)
};
export default Test;
Press the "Count up!" button.
log was output.
unmounted...
render!
ChildComponent is unchanged.
But rendered again.
why?
And how to prevent re-rendering?
thanks.
it re render because it's nested in a component that re render to prevent a nested component re render use React.memo()
const Test = () => {
const [ count, setCount ] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Count up! {count} </button>
<ChildComponent />
</div>
)
}
const ChildComponent = React.memo(() => {
useEffect(() => {
console.log('render!');
return () => console.log('unmounted...');
});
return (
<div>children</div>
)
});
export default Test;
you can read more about memo
Because the parent state is updated, the whole component will be re-rendered, include its children too, no matter if its children are changed or unchanged. You can use React.memo() to prevent your components from re-rendering. useCallback and useMemo can be used to memoize too.
See more here: How to memoize in React.