I am working with Full Calendar with React.
This is my code
<FullCalendar
ref={calendarRef}
editable={true}
headerToolbar={{ start: '', center: '', end: '' }}
plugins={[timeGridPlugin, interaction]}
initialView="timeGridWeek"
selectable={true}
select={onDateSelect}
eventDrop={onEventDrop}
eventResize={onEventResize}
eventContent={(args) => renderEventContent(args)}
allDaySlot={false}
events={events}
/>
And I have 2 callbacks eventDrop and eventResize
const onEventDrop = (event) => {
const slot = { ...event.event._def.extendedProps };
const delta = event.delta;
slot.date = new Date(slot.date);
slot.date.setHours(0, 0, 0, 0)
slot.date.setDate(slot.date.getDate() + delta.days)
slot.startTime += (delta.milliseconds / 1000)
slot.endTime += (delta.milliseconds / 1000)
updateSlot(id, slot.id, slot).then(() => {
loadSlots()
}).catch(() => {
event.revert()
})
}
const onEventResize = (event) => {
const slot = { ...event.event._def.extendedProps };
slot.startTime += (event.startDelta.milliseconds / 1000)
slot.endTime += (event.endDelta.milliseconds / 1000)
updateSlot(id, slot.id, slot).then(() => {
loadSlots()
}).catch(() => {
event.revert()
})
}
for some reason the events aren't called always, the event itself gets dragger and resized and its reflected to the view
But the callback isn't triggered
I couldn't find the exact time when its not called, but it works sometimes then it stops
I tried a different event and it works, then it stops, its mostly random
Is there any reported bug about this issue, or do I have an issue in the code?
Regards,
Related
I have a text animation script scramble.js that works fine on chrome, but stops working after a reload on safari. It started working after I cleared the cache on safari, up until I reload the page. Project was built using create-react-app. I loaded the script through html onto a different project that doesn't use react and there it works flawlessly on safari.
On checking the networks tab, on both chrome and safari, bundle.js gets a 200 or a 304 response randomly after reloading.
This is my scramble.js
window.onload = function () {
const links = document.querySelectorAll("span.cipher");
const solveMilliseconds = 1000;
const characterSelectionMilliseconds = 50;
const delayMilliseconds = 100;
const characters = [..."qwertyuioplkjhgfdsazxcvbnm!<>-_\\/[]{}—=#$&+*^?#________"];
const randomArrayElement = (arr) => {
return arr[(arr.length * Math.random()) | 0];
};
links.forEach((element) => {
element.addEventListener("mouseenter", (e) => {
const element = e.target;
scrambleText(element);
e.preventDefault();
});
});
function scrambleText(element) {
if (element.classList.contains("active") === false) {
let delay = 0;
const elementText = element.innerText;
const elementCharacters = [...elementText];
const lockMilliseconds =
delayMilliseconds * elementCharacters.length + solveMilliseconds;
element.classList.add("active");
setTimeout(() => {
element.classList.remove("active");
}, lockMilliseconds);
elementCharacters.forEach((character, index) => {
setTimeout(
() => {
let intervalId = setInterval(() => {
const randomCharacter = randomArrayElement(characters);
element.innerText = replaceCharacter(
element.innerText,
index,
randomCharacter
);
setTimeout(() => {
clearInterval(intervalId);
element.innerText = replaceCharacter(
element.innerText,
index,
elementCharacters[index]
);
}, solveMilliseconds);
}, characterSelectionMilliseconds);
},
delay === 0 ? (delay += 1) : (delay += delayMilliseconds)
);
});
}
}
function replaceCharacter(str, index, chr) {
return `${str.substring(0, index)}${chr}${str.substring(index + 1)}`;
}
};
I'm trying to create the parallax effect using the intersectionObserver API. I want the event listener to be removed from the window object when the block1 section is not intersecting. but it is not letting me remove the event for some reason
const obsCallBack = function (entries) {
entries.forEach(entry => {
let scroll = window.scrollY;
const functionParallex = () => {
let enter = entry;
if (enter.target.id === "block-1") {
let offset = window.scrollY - scroll;
portImg1.style.transform = 'translateY(' + (180 + offset * -0.30) + 'px)';
}
}
if (entry.isIntersecting &&
entry.boundingClientRect.y > 0) {
window.addEventListener('scroll', functionParallex);
}
if (!entry.isIntersecting) {
console.log(entry);
window.removeEventListener('scroll', functionParallex);
}
})
}
const obsOptions = {
root: null,
threshold: 0.01,
}
const observer = new IntersectionObserver(obsCallBack, obsOptions);
observer.observe(block1);
I am practically new to React. In this App I am using Hooks!
I've made a Countdown Timer that will show in a few seconds after logging in. I am unable to make it stop on a button click. I need some advise on this as I've been struggling for the past 2 days with this.
This is my code so far: (Please Help)
function Admin() {
const [isTimerOpen, setTimmer] = useState(false);
let history = useHistory();
// SET BY THE ADMIN
var minutesToCountDown = 0.9;
// TRANSFORM INTO SECONDS
var transformMinutesToSeconds = minutesToCountDown * 60
// KEEP A STATE
const [counterValue, setCounterValue] = useState(0);
const [isTimmerStoped, setStopTimer] = useState(false);
// FUNCTION TO HAPPEN EVERY 1 SECOND
function timeIt() {
if (isTimmerStoped === false) {
transformMinutesToSeconds--
setCounterValue(transformMinutesToSeconds)
console.log("Timer is on: ", transformMinutesToSeconds)
if (transformMinutesToSeconds === 0) {
clearInterval(interval)
setStopTimer(true)
}
} else {
setStopTimer(true)
clearInterval(interval)
}
}
// STARTS THE COUNTDOWN
var interval;
const startCountdown = () => {
interval = setInterval(timeIt, 1000)
}
const stopCountdown = () => {
console.log("Stop Timer")
setStopTimer(true);
setCounterValue(0);
setTimmer(false);
}
// ADD 0 IN FRONT ON THE TIME REMAINING
const addLeadingZeros = value => {
value = String(value);
while (value.length < 2) {
value = `0${value}`;
}
return value;
};
// CONVERT SECONDS INTO TIME REMAINING
function convertSeconds(seconds) {
var min = Math.floor(seconds / 60);
var sec = seconds % 60;
return addLeadingZeros(min) + ':' + addLeadingZeros(sec)
}
const logOutUser = () => {
logout();
return history.push(mainRoute)
}
function setTimer() {
const timer = setTimeout(() => {
setTimmer(true)
console.log('This will run after 3 seconds!')
startCountdown()
}, sessionTimeout);
return () => clearTimeout(timer);
}
useEffect(() => {
if (isTimmerStoped === false) {
console.log('Effect Starting', isTimmerStoped)
setTimer()
} else {
console.log('Effect Stopping', isTimmerStoped)
stopCountdown()
}
}, [isTimmerStoped, setStopTimer, minutesToCountDown]);
return <React.Fragment>
<CssBaseline />
<Container disableGutters maxWidth={false}>
<NavigationBar handleSignOut={logOutUser}/>
<TimerContent
timeRemaining={convertSeconds(counterValue)}
isTimerAlertOpen={isTimerOpen}
extendSessionBtn={stopCountdown}
logoutBtn={logOutUser}
clickOutsideButton={stopCountdown}/>
</Container>
</React.Fragment>
}
export default Admin;
You should do 2 things.
Make the interval variable a ref. This way it's value will be unique every where it imported. Note: Just creating a variable above the component is a bad idea because that variable will be shared between each component that imports the Admin component, which will lead to bugs.
Wrong
let interval;
function Admin() {
//... code here
// STARTS THE COUNTDOWN
// var interval; Remove from here
const startCountdown = () => {
interval = setInterval(timeIt, 1000)
}
//... code here
}
export default Admin;
Right
function Admin() {
const interval = React.useRef();
//... code here
// STARTS THE COUNTDOWN
// var interval; Remove from here
const startCountdown = () => {
interval.current = setInterval(timeIt, 1000)
}
//... code here
}
export default Admin;
Add clearInterval to your stopCountdown function. You can remove the clearInterval in the timeIt function and move it into stopCountdown. Please take a look at this codepen I made to demostrate
const stopCountdown = () => {
console.log("Stop Timer")
setStopTimer(true);
setCounterValue(0);
setTimmer(false);
clearInterval(interval.current) // Clear the interval here
}
You shouldn't maintain the intervalId returned by setInterval in a functional variable since it will be reset on re-render and you will loose the actual timer Id. You must instead use useRef hook to store the timerId
const interval = useRef(null);
...
function timeIt() {
if (isTimmerStoped === false) {
transformMinutesToSeconds--
setCounterValue(transformMinutesToSeconds)
console.log("Timer is on: ", transformMinutesToSeconds)
if (transformMinutesToSeconds === 0) {
clearInterval(interval.current)
setStopTimer(true)
}
} else {
setStopTimer(true)
clearInterval(interval.current)
}
}
...
// STARTS THE COUNTDOWN
const startCountdown = () => {
interval.current = setInterval(timeIt, 1000)
}
I want to create button to adjust/increase the props speed when my marker is moving, but in my case the speed work if the pause button clicked . what should i do if i want to click speed button and automatically update this.speed
I've declare speed prop in data and want to use in methods with setInterval
this is my data :
data: () => ({
speed: 1000
});
//my methods
moveMarker () {
this.mapLoop = setInterval(() => {
if (this.index + 1 === (this.coordinates.length)) {
this.isPlay = true
clearInterval(this.mapLoop)
new mapboxgl.Popup({ offset: 25 })
.setLngLat(this.newMarker._lngLat)
.setText('You are arrived')
.addTo(this.map)
}
const el = document.createElement('div')
el.className = 'marker'
console.log(el)
this.isPlay = false
this.map.panTo(this.coordinates[this.index])
const lat = this.coordinates[this.index][0]
const lng = this.coordinates[this.index][1]
this.newMarker.setLngLat([lat, lng])
this.newMarker.addTo(this.map)
this.index++
}, this.speed)
},
//trigger button function
forward () {
this.speed -= 500
},
You'll need to cancel the previous interval and re-start it with the new speed. For example
forward () {
clearInterval(this.mapLoop)
this.speed -= 500
this.moveMarker()
}
I made a custom audio player in React that updates the so called "Scrubber" of the audio file (progressbar) with the value based on duration of the player. Everything works fine except that if you press the play/pause button when the interval updates (mousedown -> interval updates state -> mouseup) then the button click doesn't fire.
import React, { Component } from 'react';
import ReactSVG from 'react-svg';
import VolumeButton from 'components/VolumeButton/VolumeButton.jsx';
import play from 'img/icons/play.svg';
import pause from 'img/icons/pause.svg';
import './AudioPlayer.css';
class AudioPlayer extends Component {
constructor(props) {
super(props);
this.state = {
currentTime: '00:00',
endTime: '00:00',
intervalsInitiated: false,
player: null,
playing: false,
progressBar: null
};
this.handleResize = this.handleResize.bind(this);
}
initIntervals = () => {
let intervals = setInterval(() => {
this.updateScrubber(this.state.player.currentTime);
this.updateTime(this.state.player.currentTime);
}, 100);
this.setState({
intervals: intervals,
intervalsInitiated: true
});
};
setUpAudioTime = player => {
console.log('setup time');
let playerCurrentTime = player.currentTime;
let currentTime = this.calculateTime(playerCurrentTime);
this.setState({
currentTime: currentTime,
endTime: this.calculateTime(player.duration)
});
this.updateScrubber(playerCurrentTime);
};
initPlayer = () => {
console.log('initPlayer');
// Add resize listener for showing volume on desktop
this.addResize();
this.handleResize();
// Setup correct player
let player = new Audio(this.props.audioPath);
let progressBar = document.querySelector('.ProgressBar');
// Add event for audio stopped for play button
player.addEventListener('ended', () => this.toggleAudioPlay());
// Set state to correct player
// TODO: Do this in update aswell going from one to another
this.setState({
player: player,
progressBar: progressBar
});
player.addEventListener('loadedmetadata', () =>
this.setUpAudioTime(player)
);
};
toggleAudioPlay = () => {
console.log('toggling audio');
if (!this.state.player) {
return;
}
!this.state.intervalsInitiated
? this.initIntervals()
: this.resetComponent();
this.state.playing
? this.state.player.pause()
: this.state.player.play();
this.setState({
playing: !this.state.playing
});
};
updateScrubber = playerCurrentTime => {
if (!this.state.player || !this.state.progressBar) {
return;
}
let prog = this.state.progressBar;
prog.value = playerCurrentTime / this.state.player.duration;
this.setState({
progressBar: prog
});
};
updateTime = playerCurrentTime => {
this.setState({
currentTime: this.calculateTime(playerCurrentTime)
});
};
calculateTime = lengthInSeconds => {
let minutes = Math.floor(lengthInSeconds / 60);
let seconds = Math.floor(lengthInSeconds) - minutes * 60;
let time =
(minutes < 10 ? '0' + minutes : minutes) +
':' +
(seconds < 10 ? '0' + seconds : seconds);
return time;
};
seek = event => {
if (!this.state.player || !this.state.progressBar) {
return;
}
let percent =
event.nativeEvent.offsetX / this.state.progressBar.offsetWidth;
let prog = this.state.progressBar;
prog.value = percent;
this.setState({ progressBar: prog });
let newTime = percent * this.state.player.duration;
let player = this.state.player;
player.currentTime = newTime;
this.setState({
player: player
});
this.updateTime(newTime);
};
resetComponent() {
console.log('reset component');
clearInterval(this.state.intervals);
this.setState({
intervalsInitiated: false
});
}
componentDidMount() {
this.initPlayer();
}
render() {
return (
<div
className={`AudioPlayer ${
this.state.width < 800 ? 'AudioPlayer--extended' : ''
}`}
>
<audio className="MarkerAudio" preload="metadata">
<source src={this.props.audioPath} type="audio/mp3" />
Your device doesnt support audio format.
</audio>
<div className="PlayerControls">
<div className="PlayButton" onClick={this.toggleAudioPlay}>
{this.state.playing ? (
<ReactSVG
className="AudioIcon"
svgStyle={{ height: '100%', width: '100%' }}
path={pause}
/>
) : (
<ReactSVG
className="AudioIcon"
svgStyle={{
height: '100%',
left: '2px',
position: 'relative',
width: '100%'
}}
path={play}
/>
)}
</div>
<span className="CurrentTime">
{this.state.currentTime}
</span>
<progress
onClick={this.seek}
className="ProgressBar"
value="0"
min="0"
max="1"
/>
<span>{this.state.endTime}</span>
</div>
</div>
);
}
}
export default AudioPlayer;
However, the click still works if I make sure I do a "fast-click" or if the interval isn't initiated. Any ideas on how to handle this? Should I use a onMouseDown or something instead? Or is there anyway to make sure that the interval and state change doesn't interfere with the onClick?
So for anyone wondering it had something do to with click on the icon not bubbling properly when state updated. All I had to do to fix this was to add pointer-events: none; on the icon so the click gets triggered directly on the div and not on icon.