0

I am doing a project which needs to encapsulate the letter has been typed by the user

const { useState, useEffect } = React;

function App() {
  const [usedLetter,setUsedLetter] = useState([])
  
  useEffect(()=>{
    window.addEventListener('keydown', handleKeydown);
    return () => {
      window.removeEventListener('keydown',handleKeydown);
    }
  },[])

  function handleKeydown(event){
    const letter = event.key;
    setUsedLetter(usedLetter => [...usedLetter,letter])
    console.log(usedLetter);
  }

  return (
    <div className="App">
      <p>Used letter : {usedLetter}</p>
    </div>
  );
}

ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>

This block of code produced a strange behavior. When I monitor state from react developer tool, everything was normal. The result show on the browser was normal. But when it came to the console, specifically console.log(usedLetter); the value stored in usedLetter was nothing even after I had hit a number of character. I have tried to access usedLetter through useEffect and it worked but it was inconvenient because I had to break the flow and accessed it outside of event listener callback

Can anyone tell me why this happen and how can I work around to get the real usedLetter (the one show on the browser) inside the event listener callback ?

Thanks a million

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • 2
    State is guaranteed to be stable throughout a render cycle (barring mutation) so the updated state value won't be available until the next cycle (and thus your console.log reflects the un-updated state). Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – pilchard Sep 15 '21 at 01:59
  • Yes, I expected `console.log` to be 1 step behind of the actual value that `usedLetter` hold. But reality is that all the time `usedLetter` referenced by the `console` held nothing. I think it is more related to closure like your link propose. – xuân nguyên hà Sep 15 '21 at 02:13
  • Yes, I didn't read you snippet closely enough. This is definitely a closure issue caused by the listener only being added on first mount. (The handler is declared on each render so doesn't suffer this problem, but the listener has formed a closure over it as declared on first mount). The cleanest solution is just to add `usedLetter` to the dependency array of the useEffect. `useEffect(() => {...}, [usedLetter]);` – pilchard Sep 15 '21 at 10:18
  • Tks dude, it worked like i expected :) – xuân nguyên hà Sep 17 '21 at 02:27

1 Answers1

0

useState only sets the new state which it does asynchronously in nature. there is an npm package called react-usestateref

Check out this codesandbox: https://codesandbox.io/s/sharp-bardeen-1gyo3?file=/src/App.js

I used the accepted answer from here to figure it out: React hooks: accessing up-to-date state from within a callback

Dan
  • 361
  • 1
  • 5
  • 17
  • Tks dude, work like a charm, but is this library widely used ? I am just a noob in reactJS, I think using a third party library too soon may spoil my react learning process :(. – xuân nguyên hà Sep 15 '21 at 08:01
  • That's a fair statement. I adapted the accepted answer from that SO to fit your need. Checkout this code sandbox https://codesandbox.io/s/zen-murdock-fo01f?file=/src/App.js I added in some comments to the code that I used to make it work. – Dan Sep 16 '21 at 00:15