I have 2 check boxes with state variables "isApproved, setIsApproved" and "isPlayer, setIsPlayer"
After both of these values are assigned, I need to perform some operation say getDetails(isApproved, isPlayer)
The way I know if these 2 state variables are set is by using useEffect()
useEffect(()=>{
getDetails(isApproved, isPlayer)
},[isApproved,isPlayer])
But the issue with this is, whenever user clicks on checkbox, one of these state variable value changes and again "getDetails" gets called
I want to call getDetails only for the first time after these 2 state variables are set
Any suggestions please?
Use a ref to toggle when the action is called, and avoid calling the action if the ref is true or nothing has changed from the initial values:
const initialIsApproved = false
const initialIsPlayer = false
const Demo = () => {
const [isApproved, setIsApproved] = useState(initialIsApproved)
const [isPlayer, setIsPlayer] = useState(initialIsPlayer)
const called = useRef(false)
useEffect(() => {
if(!called.current // would abort if the ref is true
&& isApproved !== initialIsApproved
&& isPlayer !== initialIsPlayer) {
called.current = true // set the ref to true
getDetails(isApproved, isPlayer)
}
}, [isApproved, isPlayer])
return (...)
}
Related
I'm trying to run multiple functions inside useEffect hook. I want the functions to run after the isConnected value is resolved to a value. Its initial value is null. after a moment it will resolve into true or false. I'm considering only the first time the isConnected value is changed. The value can change over time. I have written the following code for achieving this. I want to know if is this the correct way to achieve my goal and if there are any refactors I can do to simplify this.
const App = () => {
const {isConnected} = useNetInfo();
const wasConnectedRef = useRef(null);
useEffect(() => {
if (isConnected !== null && wasConnectedRef.current === null) {
if (isConnected) {
functionOne();
functionTwo();
}
wasConnectedRef.current = isConnected;
}
}, [isConnected]);
...
}
I would add another useState variable that you set to true once the data has been fetched and add that as a listener in the square brackets at the end of your useEffect
Every Time i try to refresh the page it returns to 0.
I'm taking the bestScore from turns when the match is equal to 6,
so basically everytime the matched cards hit 6 it will take the bestScore from the turns and save the bestScore to localStoarge and it works but when i try to refresh its gone
function App() {
const [cards, setCards] = useState([]);
const [turns, setTurns] = useState(0);
const [match, matchedCards] = useState(0);
const [bestScore, setBestScore] = useState(
localStorage.getItem("highestScoresss")
);
const [choiceOne, setChoiceOne] = useState(null); //Kullanici 1.karta basinca setChoiceOne o karti alacak ve guncelliyecek
const [choiceTwo, setChoiceTwo] = useState(null); //Kullanici 2.karta basinca setChoiceTwo o karti alacak ve guncelliyecek
const [disabled, setDisabled] = useState(false);
useEffect(() => {
if (match === 6) {
const highScore = Math.min(turns, bestScore);
setBestScore(highScore);
setBestScore(turns);
} else {
console.log("false");
}
}, [turns]);
useEffect(() => {
localStorage.setItem("highestScoresss", JSON.stringify(bestScore));
});
This Is the JSX
<div className="bilgi">
<p>Sıra: {turns}</p>
<p>Bulunan: {match}</p>
<p>En iyi Skor: {bestScore}</p>
<button onClick={shuffleCards}>Yeni Oyun</button>
</div>
</div>
The issue with your implementation is that you set state to 0 first, and then the useEffect hook runs and sets localStorage to the state value.
If you are potentially initializing your state to a value stored in localStorage then I suggest using a lazy initialization function so the initial state value is set before the initial render and eliminates the need for the additional useEffect hook to set state from storage. This reads from localStorage and returns the parsed value, or 0 if the parsed result is null or undefined.
const initializeState = () => {
return JSON.parse(localStorage.getItem("highestScoresss")) ?? 0;
};
...
const [bestScore, setBestScore] = useState(initializeState());
You will want to use a dependency array on the useEffect that is persisting the "highestScoresss" value in localStorage such that it only triggers when the bestScore state value updates and not on each and every render.
useEffect(() => {
localStorage.setItem("highestScoresss", JSON.stringify(bestScore));
}, [bestScore]);
After looking at the Code image, I think that you want that the bestScore to be set in the local storage with the key highestScores.
Your current useEffect hook implementation lacks a dependency array. You want that the localStorage should be updated every time a new bestScore is set.
For that add bestScore to the dependency array.
useEffect(() => /* YOUR OPERATION*/, [any_dependency])
Also, I recommend that you look at your first useEffect implementation again. You seem to be setting the bestScore state twice. Once with highScore and then with turns.
Recommended Reading
About Dependency Array - ReactJS Docs
I'm building a website with react(Next.js) and websocket like below.
const [selectedId, setSelectedId] = useState(null)
const ws = useRef()
useEffect(() => {
ws.current = new WebSocket(...)
ws.current.onopen = () => {...}
ws.current.onmessage = event => {
console.log(selectedId)
}
...
}, [selectedId])
Inside onmessage, console always returns same value even user set other value with setSelectedId.
I know that when websocket initialized, it will return initial value. But I need to get current state inside onmessage. Is there a solution on this? Or is it the only way to reconnect websocket to get changed state?
Your useEffect runs after the component has first mounted and keeps the initial value of selectedId in a closure which it returns on every message. To update this based on the value of selectedId you can add another effect like
useEffect(() => {
if(ws.current)
ws.current.onmessage = () => console.log(selectedId)
}, [selectedId])
which replaces the old event handler with one that uses the updated value of selectedId.
I have the following pseudo code and my issue is that since setting the state is asynchronous, by the time the state is done updating, the event has already been fired twice and I end up with duplicate objects in my state
const userList [ userList, setUserList ] = useState([]);
const onEvent = (user) => {
//then event fires again but previous setUserList has not finished setting the state
//so it is not found in userList and then I find myself with 2 of the same users in the list
if (userList.findIndex(u => u.name=== user.name) === -1)
setUserList(userList=> [...userList, {name:user.name}]);
}
The callback function for setUserList gives you the most recent value of the userList. So perform your check inside the function, not outside of it.
setUserList(previous => {
if (previous.findIndex(u => u.name === user.name) === -1) {
return [...previous, { name: user.name }];
}
return previous; // Since this line does not change the state, the component will not rerender
});
I am new to react and hooks and I am trying to set the disabled state in the below code on the condition if state.items.length is greater than 3 but I am not getting the updated values in my state object.
So I tried to set the disabled state in the useEffect hook where I get the latest values of the state.
But if I setDisabled state in useEffect it goes into an infinite loop.
Can anyone tell me what is wrong with the code?
//This is how my state object and input fields looks.
const [state, setState] = useState({
items: [],
value: "",
error: null
});
<input
className={"input " + (state.error && " has-error")}
value={state.value}
placeholder="Type or paste email addresses and press `Enter`..."
onKeyDown={handleKeyDown}
onChange={handleChange}
onPaste={handlePaste}
/>
const handleKeyDown = evt => {
if (["Enter", "Tab", ","].includes(evt.key)) {
evt.preventDefault();
var value = state.value.trim();
if (value && isValid(value)) {
setState(prev => ({
...prev,
items: [...prev.items, prev.value],
value: ""
}));
}
//if my items array which is a count of emails i.e arrays of strings is greater than 3 I want to disable the input field.
if(state.items.length > 3){
setDisabled(true);
}
}
};
useEffect(()=>{
// if I set the disabled state which is an object inside the state param it goes into an infinite loop.
passStateToParent(state);
}[state])
You should start by declaring a new variable to hold and keep track of the disabled state. (use another useState)
Then next you should use useEffect to constantly check on the length of items in current state.
I have taken code from above mentioned codesandbox as a refernce.
// use this useState hook to keep track disabled state.
const [inputDisable, setInputDisabled] = useState(false);
//use effect to check, if state item length
useEffect(() => {
const items = [...state.items];
if (items.length === 3) {
setInputDisabled(true);
}
}, [state]);
Followed by this add a new attribute named disable in your input tag and assign the value of inputDisable to it.
Refer to this codesandbox link to see the live example.
https://codesandbox.io/s/vigorous-stallman-vck52?file=/src/App.js:490-523