In my root component I have a context for keeping track of an API key that expires every once in a while:
function App() {
const [apiKey, setApiKey] = useState("");
const [timeout, setTimeout] = useState(null);
const apiKeyContextValue = {
apiKey,
setKey: setApiKey,
timeout,
setTimeout
};
return (
<apiKeyContext.Provider value={apiKeyContextValue}>
<ApiKeyForm/>
</apiKeyContext.Provider>
);
I set the api key from ApiKeyForm
, where the user just inputs an API key; however, I also want to set a timeout that'll reset apiKeyContextValue.apiKey
once it expires:
const ApiKeyForm = ()=>{
const apiKeyContext = useContext(ApiKeyContext);
const handleSubmit = e => {
//sets api key = to value in form input
apiKeyContext.setKey(e.target[0].value);
//dummy debug code for now
apiKeyContext.setTimeout(()=>{
alert('should wipe api key now');
}, 1000)
}
return (<form onSubmit={handleSubmit}>
...
</form>)
}
My issue is that when I submit my form, I get the alert from setTimeout immediately. Why is this and how would I fix this?
CodePudding user response:
Your setTimeout
identifier taken from context is a state setter, not the standard window.setTimeout
function. When you pass a function to a state setter, you're using the callback form of the state setter, which will pass the most up-to-date value in state as the argument - for example
setSomeStateValue(mostUpToDateStateValue => {
// code that relies on mostUpToDateStateValue
Which will run immediately. The same is going on with your code, only with different variable names.
Having a timeout
and setTimeout
in context state doesn't seem to be helping you any here. If you just wanted to set the state to a timeout ID that can have clearTimeout
called on it later, do:
const [timeoutId, setTimeoutId] = useState(null);
pass it down, and then do
apiKeyContext.setTimeoutId( // this references the state setter from context
setTimeout(() => { // this references window.setTimeout
alert('should wipe api key now');
}, 1000)
);
If you don't actually need to store the timeout ID in context, then you can remove it entirely and do, in ApiKeyForm:
setTimeout(() => { // this references window.setTimeout
alert('should wipe api key now');
}, 1000)