Home > Back-end >  Why does storing a timeout in React context not work?
Why does storing a timeout in React context not work?

Time:06-05

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)
  • Related