How to clear TextInput without using value - reactjs

I am not using value to not render everytime user hit a key. So my program looks like this
const debounce = (func, delay) => {
let debounceTimer;
return function () {
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer =
setTimeout(() => func.apply(context, args), delay);
}
}
const onChangeBizMsgIdrValue = React.useCallback(
(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
dispatch(setBizMsgIdrValueReducer(newValue || ''));
},
[],
);
const optimisedOnChangeBizMsgIdrValue = debounce(onChangeBizMsgIdrValue,500);
and my TextInput looks like this
<TextField defaultValue={BizMsgIdrValueRedux} onChange={optimisedOnChangeBizMsgIdrValue} style={{width: '130px'}} />
so I want to add Clear button to clear all the TextFields in Filter Component since I dont have value on TextFields i can not clear without closing the modal. Yes if i close the modal and reOpen it will be cleared but i want to achive this without closing so any ideas? I can share more if you want more about the code (NOTE: The reason of using debounce and not using value is Speed otherwise when user types there is 5 sec delay on the screen).

There is a better solution for what you want to achieve, in short; if you want to programatically clear an input in a "react" way you would need to make that input controlled.
Presumably the reason you don't want to do that is because everytime you press a key you are waiting 500ms for the input to change, I have been in your exact same situation before and the better solution is to create a handleChange function and then call that with your state change and then trigger your debounce function.
Try something like this:
const [textValue, setTextValue] = useState("");
const debounce = (func, delay) => {
let debounceTimer;
return function () {
const context = this;
const args = arguments;
clearTimeout(debounceTimer);
debounceTimer =
setTimeout(() => func.apply(context, args), delay);
}
}
const onChangeBizMsgIdrValue = React.useCallback() => {
dispatch(setBizMsgIdrValueReducer(textValue));
},
[],
);
const handleChange = (e) => {
setTextValue(e.target.value)
optimisedOnChangeBizMsgIdrValue()
}
const optimisedOnChangeBizMsgIdrValue = debounce(onChangeBizMsgIdrValue,500);
<TextField value={BizMsgIdrValueRedux} onChange={handleChange} style={{width: '130px'}} />

Related

React.JS set new state and use it in same function

I was trying to setState and use the new value inside of same function.
Functions:
const { setTasks, projects, setProjects } = useContext(TasksContext)
const [input, setInput] = useState('')
const handleNewProject = () => {
setProjects((prevState) => [...prevState, {[input]: {} }])
tasksSetter(input)
setInput('')
}
const tasksSetter = (findKey) => {
const obj = projects?.find(project => Object.keys(project)[0] === findKey)
const taskArray = Object.values(obj)
setTasks(taskArray)
}
Input:
<input type='text' value={input} onChange={(e) => setInput(e.target.value)}></input>
<button onClick={() => handleNewProject()}><PlusCircleIcon/></button>
I understand, that when we get to tasksSetter's execution my projects state hasn't been updated yet. The only way to overcome it, that I can think of, is to use useEffect:
useEffect(() => {
tasksSetter(input)
}, [projects])
But I can't really do that, because I would like to use tasksSetter in other places, as onClick function and pass another values as argument as well. Can I keep my code DRY and don't write the same code over again? If so, how?
Here's something you can do when you can't rely on the state after an update.
const handleNewProject = () => {
const newProjects = [...projects, {[input]: {} }];
setProjects(newProjects);
tasksSetter(input, newProjects);
setInput('')
}
const tasksSetter = (findKey, projects) => {
const obj = projects?.find(project => Object.keys(project)[0] === findKey)
const taskArray = Object.values(obj)
setTasks(taskArray)
}
We make a new array with the update we want, then we can use it to set it to the state and also pass it to your tasksSetter function in order to use it. You do not use the state itself but you do get the updated array out of it and the state will be updated at some point.

React setState after some seconds when user doesn't continue typing after some seconds without useReff and multiple forms

So I'm in a situation where I can't use useReff because I have a big state where I need to edit the value directly on that state, but I also don't want the state to change and render that specific component because is to heavy and I have lag rendering the component, more exactly I'm using quill-react, and this component is big so It has so lag in input with many renders.
So I have done a function that when the user stops typing after 2 seconds a function is called, and in case it doesn't stop the function set the states to true always so the function doesn't call when is checked, but is not working for some reason, I'm kinda new on this stuff.
Here is what I have done until now, and don't forget I'm iterating a list of inputs and then managing them with setState on another component, using the method: Work.Description:
const [call, setCall] = useState(false);
const [editors, setEditors] = useState({});
const [currentIndex, setCurrentIndex] = useState("");
const handleDescription = (e: any, index: number) => {
setCurrentIndex(index);
setEditors( { ...editors, [index]: e } )
setTimeout(() => setCall(true), 1000)
}
useEffect(() => {
setInterval(() => {
if (call === true) {
Work.Description(props, currentIndex, editors[currentIndex])
setCurrentIndex("")
setCall(false)
}
}, 1000)
}, [])
return (
<Container>
<Items>
{
props.resume.employmentHistory.positions.map(
(position: any, index: number) =>
<Editor
label="Description"
value={position.description && position.description || ''}
onChange={(e) => handleDescription(e, index)}
/>
)
}
</Items>
</Container >
)
As I mentioned, I want something like this not that fancy with useReff or other methods, let's see what we can do.
I think debounce might be what you're looking for, is that correct? Do something when user stops doing something for n milliseconds.
If yes then you probably don't need timeout and interval. I think you forgot to clear those timeouts/intervals by the way!
So you can do it like this (don't forget to install #types/lodash.debounce if you're using TypeScript):
import { useCallback } from 'react'
import debounce from 'lodash.debounce'
// Rename this to fit your needs.
const callWorkDescription = useCallback(
debounce((props, currentIndex, editor) => {
Work.Description(props, currentIndex, editor)
setCurrentIndex("")
}, 2000),
[]
)
const handleDescription = (e: any, index: number) => {
setCurrentIndex(index);
setEditors((prev) => {
const nextEditors = { ...editors, [index]: e }
callWorkDescription(props, index, nextEditors[index])
return nextEditors
})
}

Invalid custom hook call

I'm just a react beginner. I'm trying to create a custom hook, which will be triggered once an onClick event is triggered. By what I see, I need to use the useRef hook, to take into account if the component is rendered by first time, or if it's being re-rendered.
My code approach is the next:
const Clear = (value) => {
const useClearHook = () => {
const stateRef = useRef(value.value.state);
console.log(stateRef);
useEffect(() => {
console.log("useEffect: ");
stateRef.current = value.value.state;
stateRef.current.result = [""];
stateRef.current.secondNumber = [""];
stateRef.current.mathOp = "";
console.log(stateRef.current);
value.value.setState({
...stateRef.current,
result: value.value.state.result,
secondNumber: value.value.state.secondNumber,
mathOp: value.value.state.mathOp,
});
}, [stateRef.current]);
console.log(value.value.state);
};
return <button onClick={useClearHook}>Clear</button>;
};
Any suggestion? Maybe I might not call ...stateRef.current in setState. I'm not sure about my mistake.
Any help will be appreciated.
Thanks!
Your problem is useClearHook is not a component (the component always goes with the first capitalized letter like UseClearHook), so that's why when you call useRef in a non-component, it will throw that error. Similarly, for useEffect, you need to put it under a proper component.
The way you're using state is also not correct, you need to call useState instead
Here is a possible fix for you
const Clear = (value) => {
const [clearState, setClearState] = useState()
const useClearHook = () => {
setClearState((prevState) => ({
...prevState,
result: [""],
secondNumber: [""],
mathOp: "",
}));
};
return <button onClick={useClearHook}>Clear</button>;
};
If your states on the upper component (outside of Clear). You can try this way too
const Clear = ({value, setValue}) => {
const useClearHook = () => {
setValue((prevState) => ({
...prevState,
result: [""],
secondNumber: [""],
mathOp: "",
}));
};
return <button onClick={useClearHook}>Clear</button>;
};
Here is how we pass it
<Clear value={value} setValue={setValue} />
The declaration for setValue and value can be like this in the upper component
const [value, setValue] = useState()

How to use debounce with React

I've seen quite a few similar questions here, but none of the suggested solutions seemed to be working for me. Here's my problem (please notice that I'm very new to React, just learning it):
I have the following code in my App.js:
function App() {
const [movieSearchString, setMovieSearchString] = useState('')
const providerValue = {
moviesList: moviesList,
movieSearchString: movieSearchString,
}
const searchMovieHandler = () => {
console.log('movie handler called')
const params = {
apikey: 'somekey'
}
if (movieSearchString.length > 2) {
params.search = movieSearchString
}
debounce(() => {
console.log('deb: ' + movieSearchString)
}, 1000)
}
const movieInputChangeHandler = string => {
console.log('onMovieInputChange', string);
setMovieSearchString(string)
searchMovieHandler()
}
return (
<MoviesContext.Provider value={providerValue}>
<div className="App d-flex flex-column px-4 py-2">
<SearchBar
onMovieInputChange={movieInputChangeHandler}
/>
...Rest of the content
</div>
</MoviesContext.Provider>
);
}
In this situation all the console.logs get called EXCEPT the one that should be debounced (I tried both lodash debounce and my own, none worked; currently kept the lodash version).
So I tried to comment out that debounce call and tried to use it like that:
useEffect(() => {
console.log('use effect 1')
debounce(() => {
console.log('deb: ' + movieSearchString)
}, 1000)
}, [movieSearchString])
I'm getting the use effect 1 log when the movieSearchString changes, but not the debounced one.
So I tried to do this:
const debounced = useRef(debounce(() => {
console.log('deb: ' + movieSearchString)
}, 1000))
useEffect(() => debounced.current(movieSearchString), [movieSearchString])
In this case I'm getting the console log deb: after a second, but no movieSearchString is printed.
I don't know what else I can do here... Eventually what I want is when a user enters something in the text field, I want to send an API call with the entered string. I don't want to do it on every key stroke, of course, thus need the debounce. Any help, please?
Try to debounce the state value, not to debounce effect itself. It's easier to understand.
For example, you can use custom hook useDebounce in your project:
useDebounce.js
// borrowed from https://usehooks.com/useDebounce/
function useDebounce(value, delay) {
// State and setters for debounced value
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(
() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
// This is how we prevent debounced value from updating if value is changed ...
// .. within the delay period. Timeout gets cleared and restarted.
return () => {
clearTimeout(handler);
};
},
[value, delay] // Only re-call effect if value or delay changes
);
return debouncedValue;
}
App.js
const [movieSearchString, setMovieSearchString] = useState('')
const debouncedMovieSearchString = useDebounce(movieSearchString, 300);
const movieInputChangeHandler = string => {
setMovieSearchString(string)
}
useEffect(() => {
console.log('see useDebounceValue in action', debouncedMovieSearchString);
}, [debouncedMovieSearchString]);
useEffect(() => {
const params = {
apikey: 'somekey'
}
if (movieSearchString.length > 2) {
params.search = debouncedMovieSearchString
}
callApi(params);
}, [debouncedMovieSearchString]);
Refer to this article: https://usehooks.com/useDebounce/
You need to wrap the function you want to debounce wit the debounce() function like this:
const searchMovieHandler = debounce(() => {
console.log('movie handler called')
const params = {
apikey: 'somekey'
}
if (movieSearchString.length > 2) {
params.search = movieSearchString
}
}, 1000);

ReactJS Use SetInterval inside UseEffect Causes State Loss

So I am writing a product prototype in create-react-app, and in my App.js, inside the app() function, I have:
const [showCanvas, setShowCanvas] = useState(true)
This state is controlled by a button with an onClick function; And then I have a function, inside it, the detectDots function should be ran in an interval:
const runFaceDots = async (key, dot) => {
const net = await facemesh.load(...);
setInterval(() => {
detectDots(net, key, dot);
}, 10);
// return ()=>clearInterval(interval);};
And the detectDots function works like this:
const detectDots = async (net, key, dot) => {
...
console.log(showCanvas);
requestFrame(()=>{drawDots(..., showCanvas)});
}
}};
I have a useEffect like this:
useEffect(()=>{
runFaceDots(); return () => {clearInterval(runFaceDots)}}, [showCanvas])
And finally, I can change the state by clicking these two buttons:
return (
...
<Button
onClick={()=>{setShowCanvas(true)}}>
Show Canvas
</Button>
<Button
onClick={()=> {setShowCanvas(false)}}>
Hide Canvas
</Button>
...
</div>);
I checked a few posts online, saying that not clearing interval would cause state loss. In my case, I see some strange behaviour from useEffect: when I use onClick to setShowCanvas(false), the console shows that console.log(showCanvas) keeps switching from true to false back and forth.
a screenshot of the console message
you can see initially, the showCanvas state was true, which makes sense. But when I clicked the "hide canvas" button, and I only clicked it once, the showCanvas was set to false, and it should stay false, because I did not click the "show canvas" button.
I am very confused and hope someone could help.
Try using useCallback for runFaceDots function - https://reactjs.org/docs/hooks-reference.html#usecallback
And ensure you return the setInterval variable to clear the timer.
const runFaceDots = useCallback(async (key, dot) => {
const net = await facemesh.load(...);
const timer = setInterval(() => {
detectDots(net, key, dot);
}, 10);
return timer //this is to be used for clearing the interval
},[showCanvas])
Then change useEffect to this - running the function only if showCanvas is true
useEffect(()=>{
if (showCanvas) {
const timer = runFaceDots();
return () => {clearInterval(timer)}
}
}, [showCanvas])
Update: Using a global timer
let timer // <-- create the variable outside the component.
const MyComponent = () => {
.....
useEffect(()=>{
if (showCanvas) {
runFaceDots(); // You can remove const timer here
return () => {clearInterval(timer)}
} else {
clearInterval(timer) //<-- clear the interval when hiding
}
}, [showCanvas])
const runFaceDots = useCallback(async (key, dot) => {
const net = await facemesh.load(...);
timer = setInterval(() => { //<--- remove const and use global variable
detectDots(net, key, dot);
}, 10);
return timer //this is to be used for clearing the interval
},[showCanvas])
.....
}

Resources