I'm just playing around with the reactjs useTransition hook and trying to understand how to use it. I've gone through almost everything on the doc. The example given there is working as expected.
I'm trying to use this hook in a different context, but this time it doesn't seem to be working. Here's my code below:
import { useState, useTransition } from "react";
function Component() {
const [slow, setSlow] = useState(0);
const [fast, setFast] = useState(0);
const [isPending, startTransition] = useTransition();
const handleSlowClick = () => {
startTransition(() => {
let starting = performance.now();
while (performance.now() - starting < 5000) {
// do nothing for 5sec
}
setSlow((prev) => prev + 1);
});
};
const handleFastClick = () => {
setFast((prev) => prev + 1);
};
return (
<>
<button onClick={handleSlowClick}>slow - {slow}</button>
<button onClick={handleFastClick}>fast - {fast}</button>
</>
);
}
export default Component;
react is supposed to give everything inside the startTransition function less priority when updating states. But this isn't the case in the browser. When I click the slow button, it makes the whole web page unresponsive for 5 sec
I wonder what's happening under the hood?
[NOTE: I'm trying it inside my current NextJS project, in case it matters]
Related
I have an application in react native where i'm developing a search feature like Instagram.
It is like if user stop typing show him his query result.
my current approach is messing up redux. And sometimes it returns same element multiple times or sometime random elements which are irrelevant of that query.
right now. I'm calling search api immediately as use start typing in searchbar.
here is code below of my component.
import { getSearchDataApi } from "../../api/search/search";
import { clearSearchData, setSearchData } from "../../redux/action/search";
const SearchScreen =(props)=>{
const [autoFocus,setAutoFocus] = useState(true)
const [keyWord,setKeyWord] = useState(null)
const [isLoading,setIsLoading] = useState(false)
const [isError,setIsError] = useState(false)
const [pageNumber,setPageNumber] = useState(1)
const [loadMore,setLoadMore] = useState(true)
const loadMoreDataFunc =()=>{
if (pageNumber <= props.totalSearchPage) {
setPageNumber(pageNumber+1)
}
else {
setLoadMore(false)
}
}
const searchData = async(keyWord)=>{
console.log(keyWord,pageNumber)
try {
setIsLoading(true)
var searchResponse = await getSearchDataApi(keyWord,pageNumber)
props.setSearchData(searchResponse.data)
setIsLoading(false)
}
catch (e) {
setIsError(true)
console.log("Error --- ", e.response.data.message)
showMessage({
message: e.response.data.message,
type: "danger",
});
}
}
return (
<View>
....
</View>
)
}
const mapStateToProps = (state)=>({
searchData: state.searchReducer.searchData,
totalSearchPage: state.searchReducer.totalSearchPage,
})
export default connect(mapStateToProps,{setSearchData,clearSearchData})(SearchScreen);
I will really every thankful to someone how can help me in fixing. Appreciation in advance!
GOAL :
The goal that i want to achieve is when user stop typing then i call searchAPI with the keyword he/she entered in searchBar that's all.
I have also tried setTimeOut but that made things more worse.
The best solution to your problem is to debounce the state variable that is responsible for the user input. This way, you can use the effect hook to watch for changes on the debounced variable, and call the search API if/when conditions for the search API variables are met.
Well, I have put some effort to solve it with setTimeout once again and i have done it by following code of snippet.
useEffect(()=>{
setPageNumber(1)
props.clearSearchData()
const delayDebounceFn = setTimeout(() => {
console.log(keyWord)
if (keyWord) {
searchData(keyWord)
}
}, 500)
return () => clearTimeout(delayDebounceFn)
},[keyWord])
You can use a setInterval to create a countDown starting from 2 to 0, or 3 to 0, put it a state.
whenever user types, onChange is called, the from the callback you reset the countDown.
using useEffect with the countDown as dependency, you can open the search result whenever the countdown reaches 0. (which means the user hasn't typed anything since 2s ago)
this might help for creating the countdown https://blog.greenroots.info/how-to-create-a-countdown-timer-using-react-hooks
I am getting data from firebase in react, but I am not able to pass on that data as the variables are defined internally. Following is what I am trying to do.
function getCommentNums(item){
const formRef = database.ref(
`/comments/${form_id}/${instanceId}/${item.id}`
);
console.log('formref = ', formRef)
formRef.on('value', async(snap)=>{
const commentsArr = (await snap.val()) ?? [];
console.log('commentArr=', commentsArr.length)
setCommentLen(commentsArr.length)
})
return someNum
}
then in main return statement getcommentnums is called inside accordion
{questions.map((item, index) => (
<Accordion
key={index}
id={
"question-" +
(noOfQuestionsPerPage * (page - 1) + 1 + index)
}
question={item}
questionNo={noOfQuestionsPerPage * (page - 1) + 1 + index}
//match vs item.id
commentNums = {getCommentNums(item)}
onBlur={handleClickSave}
onClickComments={onClickComments}
onChangeAnswer={onChangeAnswer}
answers={answers}
onClickLastValue={onClickLastValue}
disabled={form.is_submitted}
/>
))}
I am trying someNum to be commentsArr.length, which is supposed to be some integer. This function is going to be called in some child component to display value of commentNums. Multiple child components are going to be on one page and each would be calling above fn to get there respective commentNums.
I have tried using set state, but that just causes infinite loop.
Can someone show me how to send commentArr.length value forward?
While you call setCommentLen(commentsArr.length) to update the commentLen state variable, your rendering code still tries to render the return value of getCommentNums, which won't work.
The proper way to implement this is to:
Modify your loader function to no longer return any value, and only update the state.
function loadCommentCount(item){
const formRef = database.ref(`/comments/${form_id}/${instanceId}/${item.id}`);
formRef.on('value', async(snap)=>{
const commentsArr = (await snap.val()) ?? [];
setCommentLen(commentsArr.length)
})
}
Call this loader function outside of the rendering code, for example when the component is created, typically in a useState handler.
useState(() => {
questions.map((item, index) => (
loadCommentCount(item);
})
}, [questions])
Then render the value from the state.
commentNums = {commentCount}
Also see:
React Not Updating Render After SetState
How to return a value from Firebase to a react component?
Firebase response is too slow
My firebase realtime push key is changed when I save it in array in react native
React-native prevent functions from executing asynchronously?
I am showing a list of users(profiles), and fetch it from some users DB.
I am in the search page which include sub pages for diffrenet filters - like which users are currently online.
Each time i am moving inside the search sub pages, i have to reset only once the main filtering variable in order ot get the correct result.
The problem is the fetch request happpend before the setState variable changed.
I saw other people asked how to fetch after, while i need it to first reset the variables of setState and the to go and fetch according to the correct values.
code:
const [isPopUpShowState,setIsPopUpShowState] = useState(false);
const [profilesloading,setProfilesLoading] = useState(<Spinner/>);
const [profilesLength,setProfilesLength] = useState(0);
const [profilesPerPage] = useState(4);
const [searchStartPoint,setSearchStartPoint] = useState(0);
const [lastUserConnIndex,setLastUserConnIndex] = useState(1);
useEffect( ()=> {
restoreStatesToDefault(); // reset states+list --> the variables doesnt changed before the the fetch
getProfilesMatchingPage(); // init profiles
},[history.location.pathname]);
const restoreStatesToDefault = () => {
list = {};
setSearchStartPoint(0);
setLastUserConnIndex(1);
setProfilesLength(0);
}
const getSearchProfilesParmsInObj = () => {
const parmsObj = {};
if(currUser.loginObj){
parmsObj['isMale'] = !currUser.loginObj.data.isMale;
parmsObj['profilesPerPage'] = profilesPerPage;
parmsObj['searchStartPoint'] = searchStartPoint;
parmsObj['lastUserConnIndex'] = lastUserConnIndex;
parmsObj['allProfiles'] = list;
}
return parmsObj;
}
const getProfilesMatchingPage = () => {
switch(history.location.pathname){
case '/search/online':
dispatch(getProfilesOnline(getSearchProfilesParmsInObj(),setProfilesLoading,setLastUserConnIndex,setProfilesLength));
break;
case '/search/pics':
dispatch(getProfilesOnlyWithPics(getSearchProfilesParmsInObj(),setProfilesLoading,setLastUserConnIndex,setSearchStartPoint,setProfilesLength));
break;
case '/search/recently':
dispatch(getProfilesRecentlyVisited(getSearchProfilesParmsInObj(),setProfilesLoading,setLastUserConnIndex,setSearchStartPoint,setProfilesLength));
break;
case '/search/news':
dispatch(getProfilesNewUsersRegistered(getSearchProfilesParmsInObj(),setProfilesLoading,setLastUserConnIndex,setSearchStartPoint,setProfilesLength));
}
}
The problem is that both functions are called within the same lifecycle of the function, so the states haven't updated yet (They are within the same closure). After your useEffect finishes, then the next render is called with the updated state values, but they are not dependencies of your useEffect so they don't trigger it to fire again (which is a good thing in this case).
Basically what you want is two useEffect -> one is triggered on path change, and that one should update state that is a dependency of another useEffect that triggers the fetch.
A simple example would be:
const [shouldFetch, setShouldFetch] = useState(false) // Set this to true if you want to fetch on initial render
useEffect( ()=> {
restoreStatesToDefault(); // reset states+list --> the variables doesnt changed before the the fetch
setShouldFetch(true);
},[history.location.pathname]);
useEffect(() => {
if (shouldFetch) {
setShouldFetch(false);
getProfilesMatchingPage(); // init profiles
}
}, [shouldFetch])
I have a website and I want to put a realtime clock on one of the pages.
I have written the code using hooks but I understand using hooks for such cases is bad for performance due to rerenders in states every second.
Is there a less resource intensive solution?
import { useEffect, useState } from 'react'
export default function Footer() {
const [time, setTime] = useState()
useEffect(() => {
setInterval(() => {
const dateObject = new Date()
const hour = dateObject.getHours()
const minute = dateObject.getMinutes()
const second = dateObject.getSeconds()
const currentTime = hour + ' : ' + minute + ' : ' + second
setTime(currentTime)
}, 1000)
}, [])
return <div>{time}</div>
}
It's definitely good that you're thinking about performance however in this situation I wouldn't change a single thing. In order to change what gets rendered to the screen (in this case the text of the clock), the component has to be re-rendered to reflect the change in it's state, which is not such a bad thing. Each time the component re-renders it's not going to cause the parent to re-render so performance-wise we're doing just fine.
So I'm currently building a tutorial, where the number of pages will continuously expand as more features are added, currently I am manually adding each file to the displaying file, i.e.
const Page0 = () => import("../../components/tutorial/Page0/index.vue");
const Page1 = () => import("../../components/tutorial/Page1/index.vue");
but obviously if this isn't very well handled once it gets really big i.e.
const Page0 = () => import("../../components/tutorial/Page0/index.vue");
...
const Page100 = () => import("../../components/tutorial/Page100/index.vue");
So I was wondering if there was a way to know let vue.js know that it should be fetching all files/folders in a certain folder and render each of them as a component with 'Page' + number name.
Ordering matters.
full code sandbox here https://codesandbox.io/s/serene-curie-it7xo?file=/pages/tutorial/_page.vue:102-247
use dynamic loading then.
in your _page.vue
function mapComponents() {
let components = {};
for (let i = 0; i < 2; i++) { // 2 should be your pages amount
components["Page" + i] = () =>
import(`../../components/tutorial/Page${i}/index.vue`);
}
return components;
}
export default {
components: mapComponents(),
computed: {
current() {
//.... other code
in your tutorial.vue
data() {
return {
pages: [...Array(2).keys()] // same here, 2 should be your pages amount
};
},
maybe just use another function to get the amount of the page, but you got the idea :)
working sample : https://codesandbox.io/s/clever-feynman-vgm93?file=/pages/tutorial.vue:280-347