jump to specific time in videojs using react hooks - javascript

I am trying to implement feature to jump 15 seconds forward or backward in video.
I am facing hard time to set the update and set the current time.
const videoNode = useRef(null);
const [currentTime, setCurrentTime] = useState(null);
const handleTimeJump = (type) => {
const player = videojs(videoNode.current);
console.log(player)
if (player) {
type === 'inc' && setCurrentTime(player.currentTime() + 15);
player.currentTime() > 15 ? setCurrentTime(player.currentTime() - 15) : setCurrentTime(0)
}
};
useEffect(() => {
const player = videojs(
videoNode.current,
videoJsOptions,
function onPlayerReady() {
console.log('onPlayerReady');
player.on('timeupdate', () => {
setCurrentTime(player.currentTime());
});
},
);
if (!videoJsOptions.sources[0].src) {
console.log('no source found');
}
return () => {
if (player) {
player.dispose();
}
};
}, []);
useEffect(() => {
const player = videojs(videoNode.current)
player.currentTime(currentTime)
}, [currentTime])
handleTimeJump is called after clicking a button.
onClick={() => handleTimeJump('inc')}

Look I haven't tested if it works but looks like it should be player.setCurrentTime(currentTime) instead of player.currentTime(currentTime)
If that works then they should have thrown an error when calling currentTime with an argument because it is not supposed to take an argument (*cough* or you could use a statically typed language *cough*)
Also the currentTime state is already in the videojs-land there's no need to create another in React-land and keep them in sync. You're dispatching a react update EVERY SECOND. Here's a higly recommended and unsolicited refactor (keeping diff as less as possible):
const videoNode = useRef(null);
const playerRef = useRef(null);
const player = playerRef.current;
const handleTimeJump = (type) => {
if (player) {
type === 'inc' && player.setCurrentTime(player.currentTime() + 15);
player.currentTime() > 15 ? player.setCurrentTime(player.currentTime() - 15) : player.setCurrentTime(0)
}
};
useEffect(() => {
playerRef.current = videojs(
videoNode.current,
videoJsOptions
);
if (!videoJsOptions.sources[0].src) {
console.log('no source found');
}
return () => {
if (player) {
player.dispose();
}
};
}, []);

Related

setInterval keeps running even after clearInterval

Thanks in advance.
This is my question : It is Quiz website for couple.
Before partner finish quiz, It send get request to server every 5s.
but the problem is even partner's answers are set, setInterval never stops.
but if I refresh my website, It works well.
Can you please give me advise?
const postAnswers = useGetResults();
const postPartnerAnswers = useGetPartnerResults();
const [myResult, setMyResult] = useState<FinalAnswer | undefined>();
const [partnerResult, setPartnerResult] = useState<FinalAnswer | undefined>();
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
const [isLoading, setIsLoading] = useState<boolean>(false);
const init = async () => {
try {
const email = localStorage.getItem('email');
const partnerEmail = localStorage.getItem('partnerEmail');
if (email !== undefined && partnerEmail !== undefined) {
// localStorage에 이메일 값들이 있으면,
const result = await postAnswers(email, partnerEmail);
const otherResult = await postPartnerAnswers(email, partnerEmail);
if (result.answers !== undefined && otherResult.answers !== undefined) {
// 몽고디비에서 받아온 값이 둘다 있으면
setMyResult(result);
setPartnerResult(otherResult);
} else {
// 몽고디비에서 받아온 값이 없으면
console.log(result.answers, otherResult.answers);
setIsLoading(true);
}
}
} catch (error) {
setErrorMessage('로딩하는 도중 에러가 발생했습니다');
console.error(error);
}
};
useEffect(() => {
init();
}, []);
useEffect(() => {
if (myResult !== undefined && partnerResult !== undefined) {
setIsLoading(false);
console.log('둘다 값이 있어요!');
console.log(isLoading);
}
}, [myResult, partnerResult]);
const timer = () => {
return setInterval(() => {
init();
console.log('isLoading', isLoading);
if (isLoading === false) {
console.log('clear');
clearInterval(timer());
}
}, 5000);
};
useEffect(() => {
if (isLoading === true) {
console.log('둘다 값이 없어요!');
timer();
}
if (isLoading === false) {
console.log('clear');
clearInterval(timer());
}
}, [isLoading]);
deployed website : https://www.couple-quiz.com/
Expanding on #Ethansocal comment:
Your code is calling clearInterval(timer()) which will create a new interval that it will immediately clear. It seems that you are confusing the API of removeEventListener and clearInterval.
clearInterval should be called with the identifier returned by setInterval.
I suggest getting rid of the timer function and rewriting your last useEffect to make it return a cleanup function when isLoading is true:
useEffect(() => {
if (isLoading) {
console.log('둘다 값이 없어요!');
const interval = setInterval(init, 5_000);
return () => { clearInterval(interval) };
} else {
console.log('clear');
}
}, [isLoading]);

React Native Socket Slow Performance

I have a markets screen where I am using sockets to update the prices of cryptocurrencies in real time. The screen contains an infinite scroller, so when the user scrolls, more cryptocurrencies load and the coins being observed by the socket changes as well. However I am noticing as the coins list is increasing, the app becomes really slow and I cannot navigate to other screens or click anywhere quickly.
I have seen a few apps achieve this infinite-scroll-live-prices logic such as CoinGecko & CoinMarketCap.
Snippet of the relevant code:
const updatePriceOfCoins = (newPrices = {}, coins = []) => {
const updatedCoins = [...coins];
let wasUpdated = false;
for (let i = 0; i < updatedCoins.length; i++) {
let coin = updatedCoins[i];
if (newPrices[coin.id] !== undefined) {
updatedCoins[i] = { ...coin, priceUsd: newPrices[coin.id] };
wasUpdated = true;
}
}
return { wasUpdated, coins: updatedCoins };
};
const MarketsScreen = ({
markets,
getMarkets,
isLoading,
isLoadingMore,
perPage,
getMoreMarkets,
hasMore,
updateMarkets
}) => {
const socket = useLivePrices(markets);
const marketsRef = useRef(markets);
useEffect(() => {
marketsRef.current = markets;
}, [markets]);
const onNewPrices = (newPrices) => {
const { wasUpdated, coins: updatedMarkets } = updatePriceOfCoins(newPrices, marketsRef.current);
if (wasUpdated) {
updateMarkets(updatedMarkets);
}
};
useEffect(() => {
getMarkets();
}, []);
useEffect(() => {
if (socket !== null) {
socket.on("new prices", onNewPrices);
}
return () => {
if (socket !== null) {
socket.off("new prices");
}
};
}, [socket]);
return (
<FlatList
data={data}
renderItem={renderDataItem}
showsVerticalScrollIndicator={false}
onEndReached={getMoreMarkets}
onEndReachedThreshold={0.5}
/>
);
};
useLivePrices hook
const useLivePrices = (coinsToWatch = []) => {
const [socket, setSocket] = useState(null);
const prevCommaSepCoins = useRef("");
useEffect(() => {
//Only initialize socket once then everytime coinsToWatch is different
//update the coins observed
if (coinsToWatch.length > 0) {
if (socket === null) {
const commaSepCoins = coinsToCommaSepIDs(coinsToWatch);
setSocket(connectToLivePricesSocket(commaSepCoins));
prevCommaSepCoins.current = commaSepCoins;
} else {
const newCommaSepCoins = coinsToCommaSepIDs(coinsToWatch);
if (prevCommaSepCoins.current !== newCommaSepCoins) {
socket.emit("update coins", newCommaSepCoins);
prevCommaSepCoins.current = newCommaSepCoins;
}
}
}
}, [coinsToWatch]);
useEffect(() => {
let unsubFocus = () => {};
let unsubBlur = () => {};
if (socket !== null) {
//pause and resume prices based on if screen is in focus
unsubFocus = navigation.addListener("focus", resumePrices);
unsubBlur = navigation.addListener("blur", pausePrices);
}
return () => {
if (socket !== null) {
socket.disconnect();
unsubFocus();
unsubBlur();
}
};
}, [socket]);
return socket;
};
I want to achieve the infinite-scroll-live-prices but not sure how to optimize the performance anymore.
I tried optimizing the performance by reducing the number of renders when price updates. I have also tried to pause and resume the socket based on if the screen is focused so that state updates are not happening while the screen is not focused.

Rendered fewer hooks than expected. This may be caused by an accidental early return statement

I'm getting this error when triggering a setState inside of a custom React hook. I'm not sure of how to fix it, can anyone show me what I'm doing wrong. It is getting the error when it hits handleSetReportState() line. How should I be setting the report state from inside the hook?
custom useinterval poll hook
export function usePoll(callback: IntervalFunction, delay: number) {
const savedCallback = useRef<IntervalFunction | null>()
useEffect(() => {
savedCallback.current = callback
}, [callback])
useEffect(() => {
function tick() {
if (savedCallback.current !== null) {
savedCallback.current()
}
}
const id = setInterval(tick, delay)
return () => clearInterval(id)
}, [delay])
}
React FC
const BankLink: React.FC = ({ report: _report }) => {
const [report, setReport] = React.useState(_report)
if ([...Statues].includes(report.status)) {
usePoll(async () => {
const initialStatus = _report.status
const { result } = await apiPost(`/links/search` });
const currentReport = result.results.filter((item: { id: string; }) => item.id === _report.id)
if (currentReport[0].status !== initialStatus) {
handleSetReportState(currentReport[0])
console.log('status changed')
} else {
console.log('status unchanged')
}
}, 5000)
}
... rest
This is because you put usePoll in if condition, see https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
You can put the condition into the callback
usePoll(async () => {
if ([...Statues].includes(report.status)) {
const initialStatus = _report.status
const { result } = await apiPost(`/links/search` });
const currentReport = result.results.filter((item: { id: string; }) => item.id === _report.id)
if (currentReport[0].status !== initialStatus) {
handleSetReportState(currentReport[0])
console.log('status changed')
} else {
console.log('status unchanged')
}
}
}, 5000)
And if the delay will affect report.status, use ref to store report.status and read from ref value in the callback.

Socket notifications

I have a screen that receives notifications from the Spring Boot backend, and I show them in a bell. When deleting a notification it deletes it well, but when another new notification arrives it loads the ones that I had already deleted.
import SockJS from 'sockjs-client';
import Stomp from 'stompjs';
// core components
const HeaderNotificacions = () => {
const [chipData, setChipData] = useState([]); //Hook where I load the notifications that come from the backend >
const historyAlerts = localStorage.getItem('notys')
? JSON.parse(localStorage.getItem('notys'))
: []; if (chipData.length === 0 && historyAlerts.length !== 0) { //I get the notifcations when I reload the browser
setChipData(historyAlerts); }
useEffect(() => {
var sock = new SockJS(
`${process.env.REACT_APP_WEB_SOCKET}mocaConsola/api/notifications`
);
let stompClient = Stomp.over(sock);
sock.onopen = function () {
/* console.log('open'); */
};
stompClient.connect({}, function (frame) {
stompClient.subscribe('/ws/alertNotification', function (greeting) {
if (stompClient !== null) {
stompClient.disconnect();
}
setChipData([
...chipData,
{
key: greeting.headers['message-id'],
label: JSON.parse(greeting.body).content,
},
]);
});
}); }, [chipData]);
localStorage.setItem('notys', JSON.stringify(chipData));
const handleDelete = (chipToDelete) => () => {
const historyAlerts = localStorage.getItem('notys') //function to delete a notification
? JSON.parse(localStorage.getItem('notys'))
: [];
setChipData((chips) =>
chips.filter((chip) => chip.key !== chipToDelete.key)
);
const local = historyAlerts.filter((chip) => chip.key !== chipToDelete.key);
localStorage.setItem('notys', JSON.stringify(local)); };
One of the problems could be that you do not disconnect from the socket, so first subscription (having initial value of chipData in closure) brings it back. Unsubscribing on effect's clean up could help, similar to:
useEffect(() => {
/* your code */
> stompClient.connect({}, function (frame) {
> subscription = stompClient.subscribe('/ws/alertNotification', function (greeting) {
> if (stompClient !== null) {
> stompClient.disconnect();
> }
>
> setChipData([
> ...chipData,
> {
> key: greeting.headers['message-id'],
> label: JSON.parse(greeting.body).content,
> },
> ]);
> });
> });
return () => subscription && subscription.unsubscribe();
}, [chipData]);
Also for performance considerations we can skip recreation of the connection/subscription each time we update chipData. We can use callback version of setChipData's argument which refer to latest value of state.
setChipData(prevData => [
> ...prevData,
> {
> key: greeting.headers['message-id'],
> label: JSON.parse(greeting.body).content,
> },
> ]);
so we can replace [chipData] to [] as second argument of useEffect and open connection only once per component load.

How do I pass index number to a react class?

Need help passing data "locationpos"= index of my Locations[] from function to class. I'm very new to React and I'm not sure what I'm doing wrong.
ERROR
Failed to compile
./src/components/data.js
Line 20:30: 'locationpos' is not defined no-undef
Search for the keywords to learn more about each error.
This error occurred during the build time and cannot be dismissed.
class Data {
constructor(locationpos) {
this.locationpos=locationpos;
this.updateData();
}
getTimes(date = null) {
date = date === null ? moment().format('DD/MM/YYYY') : date;
var data = this.getData();
return data ? data[date] : [];
}
getSpeadsheetUrl() {
return config.myData[locationpos];
}
function Daily({ locationProps = 1, root }) {
const context = useContext(ThemeContext);
const localization = useCallback(() => {
if (root && cookies.get("location") !== undefined) {
return cookies.get("location");
}
return locationProps;
}, [locationProps, root]);
const [locationState] = useState(localization());
const handleClick = event => {
window.focus();
notification.close(event.target.tag);
};
const openNav = () => {
document.getElementById("sidenav").style.width = "100%";
};
const closeNav = e => {
e.preventDefault();
document.getElementById("sidenav").style.width = "0";
};
// eslint-disable-next-line
const locationpos = locations.indexOf(locations[locationState]);
const _data = useRef(new Data(locationpos));
const getTimes = () => _data.current.getTimes();
Inside your data class, you need to use the instance variable as this.locationPos
getSpeadsheetUrl() {
return config.myData[this.locationpos];
}

Categories