checkbox state is not updating on spot - javascript

On save, I want to proceed with a function depending if a checkbox have been ticked or not. If the checkbox have been ticked, the function should proceed with the save, otherwise it should display a warning that the checkbox needs to be checked.
However, my functionality is not working. I noticed that if I console.log the checkbox boolean from the state, the state wasn't updated at the time I am calling the function.
const [checkBoxTick, setCheckBoxTick] = useState(false);
const [checkBoxError, setCheckBoxError] = useState(false);
const onSave = useCallback(async () => {
console.log(checkBoxTick);
if (checkBoxTick) {
*** go ahead and save ****
} else {
setCheckBoxError(true);
}
}, []);
<Checkbox
label="text here"
helperText="This field is required"
error={checkBoxError}
onChange={() => setCheckBoxTick(!checkBoxTick)}
/>
<Button color="primary" size="small" onClick={onSave}>
Save
</Button>
When I check the checkBox (which changes the state of checkBoxTick to true) and hit the button, the function gets called but the state of checkBoxTick is false although it should be true.
I know its a common state problem and the whole internet is recommending using useEffect(), but I couldn't make sense of how to use it in my case.
Any help is much appreciated :)
Thank you in advance.

UseCallback is memoizing the state of checkBoxTick. You need to pass the variable as a dependency to useCallback so that it gets updated, like so:
const onSave = useCallback(async () => {
console.log(checkBoxTick);
if (checkBoxTick) {
*** go ahead and save ****
} else {
setCheckBoxError(true);
}
}, [checkBoxTick]);

Related

how to prevent multiple function call in react js on value change

I want to prevent a multiple function call on user action .In my demo application I have a TABS and input field. I want to call a function on user TAB change and when user type anything on input field to call same function. Also reset the input field value when user change the tab .
Problem is my function is called twice when I change the tab. why ? I think it is due to setState only.
here is my code
https://codesandbox.io/s/sleepy-leaf-pi3ykn?file=/src/App.tsx
const AsyncCall = () => {
console.log("AsyncCallAsyncCallAsyncCallAsyncCall");
};
React.useEffect(() => {
console.log("init");
AsyncCall();
}, [value, textValue]);
React.useEffect(() => {
console.log("reset value");
setTextValue("");
}, [value]);
Step to reproduce
Run Application AsyncCall function call only one time
Type some text on input field example "abc". Every time it call function which is correct behaviour
clear console logs
Switch the second tab. if you see AsyncCall function call twice . I need to call only one time . NOT two times .this is the bug . I need to reset the input field also when user switch tabs
any way to prevent . I tried to use ref nothing works for me
I want to call function every textfield value change and tab change . But when user change Tab I need to reset the textfield value also
any suggestion ?
You can call your async call in TextField onChange like this
onChange={(e) => {
setTextValue(e.target.value);
AsyncCall();
}}
and can reset your value in Tabs onChange
handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
setTextValue("");
}
Get rid of both useEffect they are unnecessary here
According to your need
I want to call function every textfield value change and tab change .
But when user change Tab I need to reset the textfield value also
Let see the issue in your code.
React.useEffect(() => {
console.log("init");
AsyncCall();
}, [value, textValue]);
When every value and textValue changes your useEffect gonna invoke. We can remove this useEffect if we call AsyncCall() method inside your valueChange method.
For text field
const handleTextChange = () => {
setTextValue(e.target.value);
AsyncCall();
}
<TextField
id="outlined-basic"
onChange={handleTextChange} // i name it handleTextChange
value={textValue}
label="Outlined"
variant="outlined"/>
Now whenever you textValue gonna change it will call AsyncCall() method.
For tabs
const handleChange = ()=>{
setValue(newValue);
// resetting text value as you want to reset it on tab change
setTextValue("");
// Calling AsyncCall() on Tab menu change
AsyncCall();
}
<Tabs
value={value}
onChange={handleChange}
aria-label="basic tabs example"
>
</Tabs>
Now, there is no need for these useEffects
React.useEffect(() => {
console.log("init");
AsyncCall();
}, [value, textValue]);
React.useEffect(() => {
console.log("reset value");
setTextValue("");
}, [value]);
Suggestion: For textValue you can use De-bounce technique to avid unnecessary re-rendering
https://www.freecodecamp.org/news/debouncing-explained/
Paste this code in your handleChange method
setValue(newValue);
setTextValue("")
and remove you second useEffect() hook
React.useEffect(() => {
console.log("reset value");
setTextValue("");
}, [value]);

Stop requesting when changing the route in ReactJs

I make an request in react js and i get data using useSelector. So in useEffect i show the message from backend after the request.
useEffect(() => {
if (selector.response ? .message) {
console.log(selector);
message.success(selector.response?.message, 2);
setLoading(false);
}
console.log('render');
}, [selector.response]);
The code works fine, but appears an issue when i change the page(route). Clicking on another menu item i go to another page and when i come back, the useEffect is triggered again and user again sees the message from message.success(selector.response?.message, 2);. Question: How to stop showing the message each time after i come back to my route and to show the message just one time?
You have to use User Effects with Cleanup
You need to clean up the effects from the previous render or actions. Otherwise it will hold the previous state
You will find more details in official tutorial
userEffect Cleanup Process
Update Answer
import React, {useState, useEffect} from "react";
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
function handleChange(val) {
setCount(val)
}
window.addEventListener('load', handleChange(1))
return () =>{
handleChange(0);
console.log(count)
};
})
return (
<div className="App">
{count}
</div>
);
}
Here I take a count variable and setCount methods to set the count variable
Inside useEffect I create a function which will be responsible for setting up count value
Then I create an addEventListner when the page will load it will set the count value to 1
Then I call an anonymous function for clean up things which will remove the previously set value to 0
After that I set a console to just check if its sets the value
So when user come back to your page again initially it will find the default value then set the dynamic value.
You can make it more efficient way. I just give you an example
Below I share a link which will help you to handle api response
How to call api and cleanup response using react usereffect hooks
It seems like in your case your component unmounts upon page change and remounts when the user comes back. When the componet remounts, the useEffect always fires. That's simply how it works.
Answering your question, this is a simple thing you can do for your case.
// this will create a function that will return true only once
const createShouldShowSuccessMessage = () => {
let hasShownMessage = false;
()=> {
if(hasShownMessage) {
return false;
} else {
hasShownMessage = true;
return true;
}
}
};
const shouldShowSuccessMessage = createShouldShowSuccessMessage();
const SomeComponent = () => {
useEffect(() => {
if(shouldShowSuccessMessage()) {
message.success(selector.response?.message, 2);
}
setLoading(false);
}, [selector.response]);
}
I would advise you got with a better setup that performing your side effects inside your components but I hope my answer helps you till you get there

Changing a component state prop based on a Onchange of another state prop

I have a state prop that changes based on the input value(amount) the user enters(call it firstState.a). my question is how to change another state prop(fee, secondState.b) based on that value entered. I thought I could conditionally setState of the secondState in a UseEffect by having the firstState.a be the dependency which fires the useEffect whenever it changes. What am I not understanding.
pseudo code
useEffect(()=>{
if(...) {
setSecondState(secondstate.b)
} else {
setSecondState(secondstate.b)
}
}, [firstState.a])
I think this is the way that you need to use it, when the amount changes, the useeffect will be fired and if theres any change in the amount, it will update the fee as well. Or you can also make it work with onChange by setting the second state as well when you update the first state.
useEffect(() => {
setFee(amount)
}, [amount)])
Let's imaging you have 2 different states:
const [stateA, setStateA] = useState('');
const [stateB, setStateB] = useState('');
First effect to run after mounting, let's trigger the stateA change (ideally this is not the case but leave it here for the sake of example - maybe it's coming from an API call, this can be triggered maybe on user's onchange event for example of an input field):
useEffect(() => {
setStateA('value');
}, []);
Second effect will be triggered once stateA changes based on the dependency array and on its value you can call setStateB with the preferred value:
useEffect(() => {
if (stateA === 'value') {
setStateB('first condition applied');
} else {
setStateB('second condition applied');
}
}, [stateA]);
Full example of the function component's body:
const [stateA, setStateA] = useState('');
const [stateB, setStateB] = useState('');
useEffect(() => {
setStateA('value');
}, []);
useEffect(() => {
if (stateA === 'value') {
setStateB('first condition applied');
} else {
setStateB('second condition applied');
}
}, [stateA]);
Good further explanation in the documentation of Using the Effect Hook.

How to make one state change only after other state has been changed in functional component?

useEffect(() => {
if (!isEditing) {
setIsDialogOpen(false); // this happens first
}
}, [isEditing]);
useEffect(() => {
if (!isDialogOpen) {
setIsEditingTool(false); // this should happen second
}
}, [isDialogOpen]);
isEditing is a prop, coming from redux state.
The Dialog Title ("Add"/"Edit") Text is shown based on isEditingTool state (being set in the second
useState).
The isEditing state should change only after the dialog has been closed, so that the text doesn't change before dialog close.
I have tried a lot of options (setTimeout/usePrevious hook/useLayoutEffect), but its not working. Is there any way out, the only option left would be to make separate components, which I dont want.
Here I am using two states, setting isEditingTool only after dialog has been closed. there will be two different renders as per my understanding.
I have implemented the solution here: https://codesandbox.io/s/frosty-fire-xz1pt?file=/src/App.js
With hooks, we can update one state after updating another state by using useEffect.
export default function App() {
const [text1, setText1] = useState("");
const [text2, setText2] = useState("");
// update text2, when text1 updates
useEffect(() => {
if (text1) {
setText2(`${text1}, then this is text 2`);
}
}, [text1]);
return (
<div className="App">
<h1>useState callback</h1>
<h3>{text1}</h3>
<h4>{text2}</h4>
<button onClick={() => setText1("this is text 1")}>Click This</button>
</div>
);
}

How do I fix HandleToggleState?

I have a simple material-ui toggle in my react component. I want to use it to toggle state (false/true). If I start with useState(false) the first time I click the toggle it says false rather than true.
I'm wondering if another react hook would solve for this. useEffect, useCallback...
const Component = () => {
const [toggleValue, setToggleValue] = useState(false);
const handleToggleChange = () => {
setToggleValue(!toggleValue);
console.log("toggle value in handle: " + toggleValue);
};
return(
<FormControlLabel
control={
<Switch
checked={toggleValue}
onChange={handleToggleChange}
value="my toggle"
/>
}
/>
);
};
I would expect setPreseason(!preseason); to set the state opposite of what it currently is. True to false and false to true.
It probably is but when I log the state on the next line it logs the initial state and will always log the opposite of what the toggle is.
The state updater function returned by useState is asynchronous
If you need to react to state changes useEffect is the place for it
const Component = () => {
const [toggleValue, setToggleValue] = useState(false);
const handleToggleValue = () => {
setToggleValue(!toggleValue);
};
useEffect(() => {
console.log("toggleValue: " + toggleValue);
// second argument to useEffect is an array of dependencies
// this function is going to run every time one of the dependencies
// changes
}, [toggleValue])
return (
<FormControlLabel
control={
<Switch
checked={toggleValue}
onChange={handleToggleValue}
value="my toggle"
/>
}
/>
);
}
The issue is about which value toggleValue is inside the closure. Is not what you expect. Instead pass a callback to setToggleValue. You will get the current state back, that you can then change.
const handleToggleValue = () => {
setToggleValue((toggleValue) => !toggleValue);
}
You are doing it correctly, except that toggleValue is just a local variable and is not changed just by calling setToggleValue (the state will be changed, but that happens asynchronously).
You could do something like this instead:
const handleToggleValue = () => {
const newToggleValue = !toggleValue;
setToggleValue(newToggleValue);
console.log('toggleValue: ' + newToggleValue);
}
It depends what your intention is with the new toggle value. This simply fixes what you were doing with the console.log call. But you may run into further trouble after that, given that you are using the new toggle value before the state is updated. But that is for another SO question...

Categories

Resources