I'm a beginner in react and I'm just trying to dechiper a very complex code by simply logging values like props among others.
Specifically, I need to log data every time the handleScroll method is called by an event listener. The code uses lodash for throttling.
The code goes like this, but no data gets written to the console in F12:
appNode = null;
state = {
sticky: false
};
componentDidMount() {
this.props.actions.getQueues();
this.appNode = getScrollableNode(this.context.renderRoot);
if (this.appNode) {
this.appNode.addEventListener("scroll", this.handleScroll);
}
}
.
.
.
handleScroll = throttle(() => {
console.log(JSON.stringify(`this.props: ${this.props}`));
const {
actions,
query,
page,
hasMore,
loadingMore,
searchTerm,
loading
} = this.props;
const scrollTop = this.appNode.scrollTop;
The console.log there does nothing.
Any help would be greatly appreciated.
Nevermind, it wasn't a problem with the logging or anything else, but with the customized component's innerworkings.
Related
i have a problem with an hooks.
When i make
export default function Checkout() {
const { readRemoteFile } = usePapaParse();
const [parsedCsvData, setParsedCsvData] = useState([]);
const handleReadRemoteFile = file => {
readRemoteFile(file, {
complete: (results) => {
setParsedCsvData(results.data);
},
});
};
handleReadRemoteFile(CSVsource);
}
I have an infinite loop but when i make
export default function Checkout() {
const { readRemoteFile } = usePapaParse();
const [parsedCsvData, setParsedCsvData] = useState([]);
const handleReadRemoteFile = file => {
readRemoteFile(file, {
complete: (results) => {
console.log(results.data);
},
});
};
handleReadRemoteFile(CSVsource);
}
I have'nt the problem.
Have you an idea ?
Thank's Yann.
While the component code you posted is still incomplete (CSVsource is undefined, there's no return), I think the problem is visible now:
You're running handleReadRemoteFile(CSVsource) on every render (because you're calling it directly). When the file is read, you are setting parsedCsvData, which (because state updates and prop updates trigger a rerender) renders the component again, triggering handleReadRemoteFile once again...
So what's the solution?
Data fetching is a classical example of using the useEffect hook. The following code should do what you want
const { readRemoteFile } = usePapaParse();
const [parsedCsvData, setParsedCsvData] = useState([]);
useEffect(()=>{
readRemoteFile(CSVSource, {
complete: results => setParsedCsvData(results.data);
});
}, [CSVsource]); // rerun this whenever CSVSource changes.
Please read up on the useEffect hook in the React docs.
Note: there's one problem with the code I posted above. When the component is unmounted while the CSV is still loading, you will get an error "state update on unmounted component" as soon as the loading finished. To mitigate this, use the return function of useEffect in combination with a local variable.
So the final code looks like this:
useEffect(()=>{
let stillMounted = true;
readRemoteFile(CSVSource, {
complete: results => {
if(!stillMounted) return; // prevent update on unmounted component
setParsedCsvData(results.data);
}
});
return ()=>stillMounted = false; // this function is ran before the effect is ran again and on unmount
}, [CSVsource]);
In my React.js project, I am trying to show error message when user select wrong item in the drop down box. Following is the code executed when user select wrong item in dropdown box. Current code is working well. But I think this is not recommend way to change state.(It seems changing "error" state directly.)
I hope to uncomment line 3 and want to comment line 2.(I think this is correct way as it is using instance of "error" state. I might be wrong, but I think so.) But it is not working well.
What is the reason? How to change error state by using it's instance? I don't want to change state directly.
handleErrorChange = async (policy, error) => {
const { errors } = this.state; // I don't want this way.
// const errors = { ...this.state.errors }; // I hope to use this way, but not working.
if (policy) {
errors[policy.id] = error;
this.setState({ errors });
// console.log(errors);
}
this.updateErrors();
}
So finally, I want do like following.
handleErrorChange = async (policy, error) => {
// const { errors } = this.state;
const errors = { ...this.state.errors };
if (policy) {
errors[policy.id] = error;
this.setState({ errors });
// console.log(errors);
}
this.updateErrors();
}
I have been working on a real-time notification system for my nextjs app. It is working great, and I call it like this in each page:
export default function Bot({user}) {
startNotifications(user)
}
The function itself looks like this
const userID = getUserID(user)
const pubnub = new PubNub({
subscribeKey: subKey,
uuid: userID
});
const { asPath } = useRouter()
pubnub.unsubscribeAll();
pubnub.addListener({
signal: function(signal) {
const message = signal.message
if (message[0] == "bot") {
Notiflix.Notify.Info(notificationMessage);
if (check if url is correct here) {
callback()
return
}
}
}
})
pubnub.subscribe({
channels: ["announcements", userID.toString()],
});
But when i try to add a callback funtion to change the state, depending on notification like this:
export default function Bot({user}) {
startNotifications(user, changeState)
const [value, setValue] = useState(true)
function changeState(){
console.log("changing state")
setValue(false)
}
}
The code works fine, and the state is getting changed, but for some reason the code loops over itself and doubles the number of listeners each notification. In the startNotifications function I even tried to unsubscribe and resubscribe, but that doesn't work either. It looks like this
(First notification)
And then if I do it a couple more times the 4. Notification looks like this:
I am pretty much at the end of trying. I have tried to implement the startNotifications in the same file, but that does the same thing.
If anyone has a solution to this (its probably something very obvious that I don't know) please let me know. Have a great day.
One thing that you are missing is putting the whole startNotifications inside an useEffect hook. Without it, all of this code is executed each time the component rerenders.
export default function Bot({user}) {
useEffect(() => startNotifications(user), []);
// rest of the code
}
I'm trying to make an e-commerce app using expo with typescript and I'm having issues with loading my categories page.
There's two ways which one can access the category page
Through bottom nav - this one works fine
Through buttons on the home page
Trying to setState based off the params that's coming in through the homepage but it's giving me the Too many re-renders error.
Below is the code that I currently have for it.
The default needs to be there because of the first method of entering the category page, I've tried putting a conditional as the default category, but category wont change the second time if being accessed through the home page again.
export default function CategoryList(props: Props) {
let { params } = useRoute<StackRouteProp<'Home'>>()
const [currentCategory, setCategory] = useState({categories: Categories[0]});
if (params) {
setCategory({categories: params.categories})
}
}
You can also use useEffect with useRef:
export default function CategoryList(props: Props) {
let { params } = useRoute<StackRouteProp<'Home'>>()
const [currentCategory, setCategory] = useState({categories: Categories[0]});
const canSet = useRef(true)
useEffect(() => {
if (params && canSet.current) {
setCategory({ categories: params.categories })
canSet.current = false
} else {
canSet.current = true
}
}, [params])
Everytime you call setCategory, it will re-render the component. Then, every time you render the component, if params exists, it will call setCategory again, which will cause another render, which calls setCategory again, and so on and on until it hits React's render limit.
One possible way to work around this would be to add a boolean to set after the first time which will prevent it from going inside the if block the second time:
const [currentCategory, setCategory] = useState({categories: Categories[0]});
const [hasParamsCategory, setParamsCategory] = useState(false);
if (params && !hasParamsCategory) {
setCategory({categories: params.categories});
setParamsCategory(true);
}
This still isn't perfect because in general it's not a good practice to directly call state-changing functions during render, but this will serve as a quick and dirty workaround.
I am getting the above error on the following code. Will anyone help me find out what could be wrong with this code? Thanks
const [userData, setUserData] = useState({});
useEffect(() => {
const user = auth.getCurrentUser();
setUserData(user);
});
This is the getCurrentUser function
export function getCurrentUser() {
try {
const jwt = localStorage.getItem(tokenKey);
return jwtDecode(jwt);
} catch (ex) {
return null;
}
}
You should be very cautious while using useEffect.
Below is what you have used in your implementation. Which is also the problem why the error.
useEffect(() => {
const user = auth.getCurrentUser();
setUserData(user);
});
To understand what exactly happening just a background of useEffect. When useEffect is being used without any dependencies it'll execute every time the component gets rendered. Please have a look here for more info.
So in this case in useEffect you are trying to update the state of the component which will cause the component to re-render, as discussed again useEffect will get called again state gets updated and the loop goes on.
setUserData() -> component re-render -> setUserData() -> component re-render -> setUserData() -> component re-render ....the cycle goes on
So ideally you should be getting the current user details only once after the component mounted and this can be done by passing [] as dependency to useEffect as below
useEffect(() => {
const user = auth.getCurrentUser();
setUserData(user);
}, []);
Hope this helps.
You have to try this
useEffect(() => {
function getData() {
const user = auth.getCurrentUser();
setUserData(user);
}
getData()
},[]);