Is there any way to setState in react immediately and not wait for re-render. I have 2 cases where if the values are set immediately, I can solve half the issues.
The first thing is making visibility to false immediately
const [visibilty, setVisibility] = useState(false);
setVisibility(false);
and the other is I'm picking the current URL from the window.location.href.But when I'm setting URLs like this, I'm not able to pick URLs when users navigate through the history.push(Where page doesn't refresh so........need one more render the setState to current URL)
const [currentUrl, setCurrentURL] = useState("");
let url = window.location.href;
setCurrentURL(url);
I'm new to programming,so appreciate any help.
Related
I am building an app to understand the useState hook. This app simply has a form for entering username. I am trying to save the entered username. So, I have used react useState. And I tried to await the updating function of the useState in the event handler.
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
And when I tried to log the username it doesn't show us the current state but the previous state. Why?
const usernameChangeHandler = async (event) => {
await setEnteredUsername(event.target.value);
console.log(enteredUsername, enteredAge);
};
enteredUsername is never going to change. It's a closure variable that's local to this single time you rendered the component. It's usually a const, but even if it was made with let, setEnteredUsername does not even attempt to change its value. What setEnteredUsername does is ask react to rerender the component. When the render eventually happens, a new local variable will be created with the new value, but code from your old render has no access to that.
If you need to run some code after calling setEnteredUsername, but you don't actually care if the component has rerendered yet, the just use the value in event.target.value, since you know that's going to be the new value of the state:
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
console.log(event.target.value, enteredAge);
}
If instead you need to make make sure that the component has rerendered and then do something after that, you can put your code in a useEffect. Effects run after rendering, and you can use the dependency array to make it only run if the values you care about have changed:
const [enteredUsername, setEnteredUsername] = useState('');
useEffect(() => {
console.log('rendering complete, with new username', enteredUsername);
}, [enteredUsername]);
const usernameChangeHandler = (event) => {
setEnteredUsername(event.target.value);
};
the act of setting state is asynchronous; therefore, console logging directly after setting your state will not accurately provide you with how state currently looks. Instead as many have suggested you can utilize the useEffect lifecycle hook to listen for changes in your enteredUserName state like so:
useEffect(() => {
console.log(enteredUsername);
}, [enteredUsername]);
listening for changes within the useEffect will allow you to create side effects once state has updated and caused your component to rerender. This in turn will trigger your useEffect with the enteredUsername dependency, as the enteredUserName state has changed.
I'm building a widget and adding it on a react website. Widget does specific tasks based on the URL.
Let's say, I want widget to appear exactly when URL is www.company.com. But the widget appears on "www.company.com/welcome" as well if the user navigated through history.push/ or replace.
Only when a state Change happens inside widget on www.company.com/welcome, it realizes, "oh I am not supposed to appear here".
I'm quite new to programming. I want the widget to re-render everytime the parent website URL changes(and not wait for any state change inside Widget). How to do this?
Currently, this is how finding current URL. I tried 2 approaches.
In the first approach here, on some state change, widget realises it shouldn't come here.....but it requires that state change.
const Main =() ={
let currentUrl = window.location.href;
}
In the 2nd approach, it doesn't work even after a state change.
useEffect(() => {
let url = window.location.href;
setCurrentURL(url)
},[currentUrl]);
3rd approach suggested by user, behaves exactly like the above issue.
const Main =() ={
const [url, setUrl] = useState(window.location.href);
}
Something interesting : The widget works properly when it is added on a html website when moved between pages or when navigation is through window.location ="/"
Only when Parent website is navigated through history.push or history.replace and widget doesn't seem to render.
If it's a stupid mistake, feel free to let me know. I have wasted too much time on this problem already and would appreciate any sort of help
have you tried looking at useEffect?
DOC: https://reactjs.org/docs/hooks-effect.html
useEffect(() => {
//****DO SOMETHING HERE WITH YOUR WIDGITS HERE****
}, []); //
you are currently storing the
let currentUrl = window.location.href;
in a variable are you sure you don't want to use useState() hook?
import { useState } from "react";
const Main =() ={
const [url, setUrl] = useState(window.location.href);
}
I have a button that use this function to delete a value from firebase, these values are shown in a flatlist in the same page, the function works good but to see the changes I need to reload the page or go back and return on it.
This is my function
const onDelete = (sizeId) => {
firebase.firestore()
.collection("Measures")
.doc(firebase.auth().currentUser.uid)
.collection(route.params.size)
.doc(sizeId)
.delete()
How can I make it refresh after the changes so I can see the new flatlist without the value that I deleted?
If you use a React.Class you can try using a PureComponent. Upon removing the value from the state array, you could use a boolean state that you flip between true and false to trigger a re-render.
If you are using a functional component and you keep the data in a useState hook, you can cause a re-render via useEffect on that state upon change.
const [arr, setArr] = useState([]);
useEffect( () => {}, [arr] ); //This listens for state changes on arr and will cause a re-render upon change.
This would only work if you aren't setting state in your useEffect as it will cause an infinite loop if you call setArr() in your useEffect. In which case I have used a simple boolean that I flip back and forth when I want a re-render.
I'm trying to get a functional component to force a rerender whenever the testFn executes. I thought to use state to do this (if there's a better way then please speak up), which appears to successfully force a rerender but only twice, then nothing.
I built a simple demo to emulate the issue as using my real app is too difficult to demonstrate but the same principles should presumably apply (my real demo fetches data when the function executes and displays it on the page, but it's not rerendering and I have to refresh the page to see the new data, hence why I want to trigger a rerender).
import React, { useState } from "react";
const App = () => {
const [, rerender] = useState(false);
const testFn = () => {
console.log("test Fn");
rerender(true);
};
return (
<div>
<p>test</p>
<button onClick={testFn}>clickk</button>
{console.log("render")}
</div>
);
};
export default App;
I've also made a Stackblitz demo for conveinence.
Can anyone solve this demo or think of a better way of implementing it?
Thanks for any help here.
It triggers a re-render when the state changes.
The first time you click the button you change the state from false to true so a rerender is triggered.
Subsequent clicks you change it from true to true which isn't a change, so it doesn't.
You could toggle it:
const [render, rerender] = useState(false);
and
rerender(!render);
so it actually changes.
… but this smells of being an XY Problem, and you should probably be changing something which is actually being rendered.
State is the right way, since state changes are the primary way to cause re-renders. I'd increment a counter:
const [, setCount] = useState(0);
// force a re-render by running:
setCount(c => c + 1)
But this is still a very odd thing to do. In almost all cases, a much more elegant solution will be implementable, such as by putting data that changes into state, and calling the state setter when the data updates.
I can't seem to find a good pattern for one scenario...
Lets say we have this kind of order in component:
const component = ({propslist}) => {
const [state1, changeState1] = useState();
const [state2, changeState2] = useState();
useEffect(() => {
//this effect does something and updates state 1
const someVar = someOperation();
changeState1(someVar);
});
useEffect(() => {
//this effect does something and updates state 2
const someVar = someOtherOperation();
changeState2(someVar);
});
return (<div>...</div>);
}
Now, if i understand correctly and from what i see in my tests, the moment first useEffect changes the state, the component will re-render.
The thing that makes me think so is that if i put it that way i get error: Rendered fewer hooks than expected.
2 questions:
Is it the case that the moment something changes the state that component stops execution and goes into re-render?
How to change multiple states from multiple effects? Is there some good pattern about it? Should we have remodel things to pack all state changes into single effects hook or pack all 'chunks' into single state monolith object and change it from single place?
Any suggestions & best practices would be appreciated.
[UPDATE]
My apologies.
I was testing different versions and posted wrong code example.
This is the code example that causes error Rendered fewer hooks than expected.:
const component = ({propslist}) => {
const [state1, changeState1] = useState();
const [state2, changeState2] = useState();
if(someCondition)
changeState1(something);
useEffect(() => {
//this effect does something and updates state 2
const someVar = someOperation();
changeState2(someVar);
});
return (<div>...</div>);
}
So, i guess call to changeState1() starts re-render immediately and prevents useEffect from being called thus causing the error. Right?
To avoid the "Rendered fewer hooks than expected" error, you need to put your useEffect hooks after the if statement.