Losing selected text focus on Value change - javascript

I am writing a time display component. The value changes every second, whenever I am trying to copy the text it loses the focus because value changes. Current Epoch for example in below image
const [currentEpoch, setCurrentEpoch] = useState(initialEpoch);
const [localTime, setLocalTime] = useState(initialLocalTime);
const [utcTime, setUtcTime] = useState(initialUtcTime);
const [convertEpoch, setConvertEpoch] = useState<{ localDate: string, utcDate: string } | null>();
useEffect(() => {
setInterval(() => {
setCurrentEpoch(Math.floor(Date.now() / 1000));
setLocalTime(new Date().toString());
setUtcTime(new Date().toUTCString());
}, 1000);
}, []);

Related

Show countdown when holding button then show alert

I'm trying to create a button, which on hold shows a countdown of 3 seconds, if kept on hold for 3 seconds it shows an alert, but for some reason the countdown doesn't reset properly AND the alert fires up anyway, at every click (after the timeout)
my code is:
const [showCountdown, setShowCountdown] = useState(false)
const [holdTimer, setHoldTimer] = useState(3)
var timer = 0, interval;
const refreshDown = () => {
setShowCountdown(true)
interval = setInterval(function(){
setHoldTimer((t) => t - 1)
},1000);
timer = setTimeout(function(){
if (confirm('Refresh page?')){
window.location.reload()
}
},3000)
}
const refreshUp = () => {
clearTimeout(timer)
clearInterval(interval)
setShowCountdown(false)
setHoldTimer(3)
}
my html button has these two:
<svg onMouseDown={() => refreshDown()} onMouseUp={() => refreshUp()}>
...
</svg>
Have you tried with useRef ?
const timer = useRef();
const interval = useRef();
const refreshDown = () => {
setShowCountdown(true);
interval.current = setInterval(function () {
setHoldTimer((t) => t - 1);
}, 1000);
timer.current = setTimeout(function () {
if (confirm("Refresh page?")) {
window.location.reload();
}
}, 3000);
};
const refreshUp = () => {
clearTimeout(timer.current);
clearInterval(interval.current);
setShowCountdown(false);
setHoldTimer(3);
};
React component is rerendered each time state or props are changed. When setHoldTimer is executed, it changes the state. It causes reexecuttion of component’s code, so local variables “timer” and “interval” are declared again with values “0” and “undefined”, also new “refreshUp” function is created referencing new “timer” and “interval”. When you release the mouse, new “refreshUp” is called, interval and timeout are not cleared.
Try to define timer and interval as a state or with “useRef”. This way they will not be redefined during rerender.

now.getTime() inside useEffect doesn't work

I wanted to record how many time passed until user click the button.
My plan was like: (the time when user enter the page) - (the time when user click the button) = how many milliseoconds passed!
but my code had an bug and error. my useEffect didn't go what I wanted..
It didn't memorized the time when user entered the page.
console said :
👉undefined
and when first hit:
-1611798597788milliseconds passed
(it worked well as I planned when second hit. weird 🤔)
here's my code:
const UserTimer = () => {
const [startTime, setStartTime] = useState(null);
const [endTime, setEndTime] = useState(0);
const [diff, setDiff] = useState(0);
useEffect(() => {
let now = new Date();
setStartTime(now.getTime());
console.log('👉' + startTime);
}, []);
const onClick = () => {
setEndTime(Date.now());
setDiff(endTime - startTime);
setStartTime(endTime);
};
return (
<div>
<p>{diff}milliseconds passed</p>
<button onClick={onClick}>click here</button>
</div>
);
};
thank you in advance.
setState does not happen synchronously, so you will not see it change instantly. Best to set a variable then use that variable for your math.
Maybe something like this?
const UserTimer = () => {
const [startTime, setStartTime] = React.useState(null);
const [diff, setDiff] = React.useState(0);
React.useEffect(() => {
const now = Date.now()
setStartTime(Date.now());
console.log('👉' + now);
}, []);
const onClick = () => {
const endTime = Date.now()
setDiff(endTime - startTime);
setStartTime(endTime);
};
return (
<div>
<p>{diff}milliseconds passed</p>
<button onClick={onClick}>click here</button>
</div>
);
};
That's because your startTime state is set as null here:
const [startTime, setStartTime] = useState(null);
So first click it is still null, so your setDiff(endTime - startTime); returns the negative number. You can try to solve it using:
useEffect(() => {
let now = new Date();
setStartTime(now.getTime());
console.log('👉' + startTime);
}, [startTime]);

Cannot empty array in ReastJS useEffect hook

I'm having this table view in React where I fetch an array from API and display it. When the user types something on the table search box, I'm trying to clear the current state array and render the completely new result. But for some reason, the result keeps getting appended to the current set of results.
Here's my code:
const Layout = () => {
var infiniteScrollTimeout = true;
const [apiList, setapiList] = useState([]);
//invoked from child.
const search = (searchParameter) => {
//Clearing the apiList to load new one but the console log after setApiList still logs the old list
setapiList([]); // tried setApiList([...[]]), copying the apiList to another var and emptying it and then setting it too.
console.log(apiList); //logs the old one.
loadApiResults(searchParameter);
};
let url =
AppConfig.api_url + (searchParameter || "");
const loadApiResults = async (searchParameter) => {
let response = await fetch(url + formurlencoded(requestObject), {
method: "get",
headers: headers,
});
let ApiResult = await response.json(); // read response body and parse as JSON
if (ApiResult.status == true) {
//Set the url for next fetch when user scrolls to bottom.
url = ApiResult.next_page_url + (searchParameter || "");
let data;
data = ApiResult.data;
setapiList([...data]);
}
}
useEffect(() => {
loadApiResults();
document.getElementById("myscroll").addEventListener("scroll", () => {
if (
document.getElementById("myscroll").scrollTop +
document.getElementById("myscroll").clientHeight >=
document.getElementById("myscroll").scrollHeight - 10
) {
if (infiniteScrollTimeout == true) {
console.log("END OF PAGE");
loadApiResults();
infiniteScrollTimeout = false;
setTimeout(() => {
infiniteScrollTimeout = true;
}, 1000);
}
}
});
}, []);
return (
<ContentContainer>
<Table
...
/>
</ContentContainer>
);
};
export default Layout;
What am I doing wrong?
UPDATE: I do see a brief moment of the state being reset, on calling the loadApiResult again after resetting the state. The old state comes back. If I remove the call to loadApiResult, the table render stays empty.
add apiList in array as the second parameter in useEffect
You need to use the dependencies feature in your useEffect function
const [searchParameter, setSearchParameter] = useState("");
... mode code ...
useEffect(() => {
loadApiResults();
... more code ...
}, [searchParameter]);
useEffect will automatically trigger whenever the value of searchParameter changes, assuming your input uses setSearchParameter on change

SetInterval has to be triggered whenever the time is getting changed?

Am using setInterval for updating the time, for that i have set 60000s as interval. whenever i load the page i will get the time and time is getting updated after 1 minute from when i have refreshed the page but not with respective to the time. Hence there is some delay in updating the time.
const UpdateTime= (props) => {
let date = new Date();
const [dateTime, setDateTime] = useState({
curTime: date.toLocaleDateString(),
timeStr: date.toLocaleTimeString().replace(/:\d{2}\s/, " "),
curDay: date.toLocaleDateString("en-US", { weekday: "short" })
});
useEffect(() => {
const interval = setInterval(() => {
let date = new Date();
setDateTime({
curTime: date.toLocaleDateString(),
timeStr: date.toLocaleTimeString().replace(/:\d{2}\s/, " "),
curDay: date.toLocaleDateString("en-US", { weekday: "short" })
});
},
60000
);
return () => { clearInterval(interval) }
}, [])
}
after loading the page, whenever the date.getSeconds() is reaching 00 from there i have to trigger the setinterval function.
Well, if you want to go in this way, I guess there's no other solution than this:
Because you can't 'trigger' intervals just simulating it (How to trigger a setInterval function on a user click?), you can use function which will be called by the interval. Then you can call that function when the date.getSeconds() reach 00. However for this you will need a second interval which triggered every second until it reach the 00 and than you can clear it. After you called it and the second is 00 you set the interval for 60 seconds.
It's necessary to reset the 60s interval or it will be inaccurate again.
i have used setTimeOut to call the interval function whenever date.getSeconds() becomes 0 for the first time. But the problem what i am facing is i couldn't able to clear the interval. I am getting more "inside timeout and inside interval" in the console its not clearing the previous log, i am expecting timeout to be one and whenever the setInterval runs then i have to get the "inside interval" in the console
const DateTime = (props) => {
let date = new Date();
let initialDiff = (date.getSeconds() == 0) ? 0 : (59 - date.getSeconds());
setTimeout(() => {
triggerDateUpdate()
console.log("inside timeout")
}, initialDiff * 1000);
const [dateTime, setDateTime] = useState({
curTime: date.toLocaleDateString(),
timeStr: date.toLocaleTimeString().replace(/:\d{2}\s/, " "),
curDay: date.toLocaleDateString("en-US", { weekday: "short" })
});
const interval = () => {
console.log("inside interval")
let date = new Date();
setDateTime({
curTime: date.toLocaleDateString(),
timeStr: date.toLocaleTimeString().replace(/:\d{2}\s/, " "),
curDay: date.toLocaleDateString("en-US", { weekday: "short" })
});
}
let triggerDateUpdate = () => {
interval();
setInterval(() => {
interval();
}, 60000)
}

React autocomplete in a textarea like in an IDE (e.g. VS Code, Atom)

I try to code an auto completion feature like in the gif in React.
So suggestions appear while writing text.
However all packages I could find so far work
a) only in the beginning of the input/textarea (e.g. react-autosuggest)
b) or need a trigger character (like # or #) to open (e.g. react-textarea-autocomplete)
Do I miss some React limitation? Any hints / packages?
We ended up using the fantastic editor Slate.js.
The Mentions example can easily be changed so that the suggestions are triggered by any character (not only '#'). There you go: perfect auto suggest.
I'm actually having the same problem in regards to needing a textarea implementation, but I can help with autocomplete triggering behavior.
We have an implementation of template variables that look like this {{person.name}} which get resolved into whatever the actual value is.
In regards to the autocompletion being triggered only on the first word, you can get around that with a couple modifications to the required functions.
For instance my required functions look like this. (not a completely working example, but all the important bits)
const templateVars = Object.values(TemplateVarMap);
const variables = templateVars.map((templateVar) => {
return {
name: templateVar,
};
});
//This func, onChange, and onSuggestionSelected/Highlight are the important
//parts. We essentially grab the full input string, then slice down to our
//autocomplete token and do the same for the search so it filters as you type
const getSuggestions = (value) => {
const sliceIndex = value
.trim()
.toLowerCase()
.lastIndexOf('{{'); //activate autocomplete token
const inputValue = value
.trim()
.toLowerCase()
.slice(sliceIndex + 2); //+2 to skip over the {{
const inputLength = inputValue.length;
//show every template variable option once '{{' is typed, then filter as
//they continue to type
return inputLength === 0
? variables
: variables.filter(
(variable) => variable.name.toLowerCase().slice(0, inputValue.length) === inputValue
);
};
const getSuggestionValue = (suggestion) => suggestion.name;
const renderSuggestion = (suggestion) => <div>{suggestion.name}</div>;
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value),
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
onChange = (event, { newValue }) => {
//onChange fires on highlight / selection and tries to wipe
//the entire input to the suggested variable, so if our value
//is exactly a template variable, don't wipe it
if (templateVars.includes(newValue)) {
return;
}
this.setState({
value: newValue,
});
};
//These both need to do similar things because one is a click selection
//and the other is selection using the arrow keys + enter, we are essentially
//manually going through the input and only putting the variable into the
//string instead of replacing it outright.
onSuggestionHighlighted = ({ suggestion }) => {
if (!suggestion) {
return;
}
const { value } = this.state;
const sliceIndex = value.lastIndexOf('{{') + 2;
const currentVal = value.slice(0, sliceIndex);
const newValue = currentVal.concat(suggestion.name) + '}}';
this.setState({ value: newValue });
};
onSuggestionSelected = (event, { suggestionValue }) => {
const { value } = this.state;
const sliceIndex = value.lastIndexOf('{{') + 2;
const currentVal = value.slice(0, sliceIndex);
const newValue = currentVal.concat(suggestionValue) + '}}';
this.setState({ value: newValue });
};
const inputProps = {
value: this.state.value,
onChange: this.onChange,
};
render() {
return (
<Autosuggest
suggestions={this.state.suggestions}
onSuggestionSelected={this.onSubjectSuggestionSelected}
onSuggestionHighlighted={this.onSubjectSuggestionHighlighted}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
)
}
This lets me type something like This is some text with a {{ and have autocomplete pop up, upon choosing a selection it should go to This is some text with a {{person.name}}.
The only problem here is that it requires the final two characters in the input to be {{ (or whatever your token is) for the autocomplete box to come up. I'm still playing with cursor movement and slicing the string around in different ways so if I edit a template thats not at the end the box still pops up.
Hopefully this helps.
You can try react-predictive-text

Categories

Resources