Home > OS >  useState() setter not triggering inside an async function (electron)
useState() setter not triggering inside an async function (electron)

Time:04-25

I have been battling with this for a while and I would really appreciate any pointers you could give me.

I am building a simple electron app using react for a custom macro keypad I made using a programmable microcontroller. Communication is handled via serial port and works as intended. I would like to be able to select one of the buttons by either clicking on the GUI (working) or by pressing the actual key directly on the keypad (not working).

To be more specific, the setter gets properly called on click but not on serial port read.

import './App.css';
import Key from './components/Key';
import styled from "styled-components";
import React, { useEffect, useState } from 'react';

const KeyHolder = styled.div`
  display: grid;
  grid-template-columns: repeat(4, 100px);
  gap: 1rem;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
`;

const App = () => {

  const [isPortOpen, setPortOpen] = useState(false);
  const [selectedKey, setSelectedKey] = useState('');

  useEffect(() => console.log('selectedKey', selectedKey), [selectedKey]);
  useEffect(() => console.log(isPortOpen), [isPortOpen]);

  const connect = async () => {
    try {
      const ports = await navigator.serial.getPorts();
      const port = ports[0];
      await port.open({ baudRate: 9600 });
      setPortOpen(true);

      // eslint-disable-next-line no-undef
      const decoder = new TextDecoderStream();
      port.readable.pipeTo(decoder.writable);
      const inputStream = decoder.readable;
      const reader = inputStream.getReader();

      while (true) {
        const { value, done } = await reader.read();
        if (value) {
          console.log("key pressed: ",value); // This correctly logs the pressed key
          setSelectedKey(value); // This setter does not trigger the useEffect()
        }
        if (done) {
          console.log('DONE');
        }
      }
    } catch (error) {
      console.warn(error);
    }
  }

  if (!isPortOpen) {
    connect();
  }

  return (
    <div className="App">
      <header className="App-header">
        <KeyHolder>
          {/* All this setters work as intended and trigger the useEffect() */}
          <Key isPressed={selectedKey === '5'} onClick={() => setSelectedKey('5')} />
          <Key isPressed={selectedKey === '6'} onClick={() => setSelectedKey('6')} />
          <Key isPressed={selectedKey === '7'} onClick={() => setSelectedKey('7')} />
          <Key isPressed={selectedKey === '8'} onClick={() => setSelectedKey('8')} />
          <Key isPressed={selectedKey === '1'} onClick={() => setSelectedKey('1')} />
          <Key isPressed={selectedKey === '2'} onClick={() => setSelectedKey('2')} />
          <Key isPressed={selectedKey === '3'} onClick={() => setSelectedKey('3')} />
          <Key isPressed={selectedKey === '4'} onClick={() => setSelectedKey('4')} />
        </KeyHolder>
      </header>
    </div>
  );
}

export default App;

Could the problem be that the setter is being called from within an async function? If that is the case, coud you point me to some resources on how to avoid that issue?

Thank you very much in advance.

CodePudding user response:

You can place your connect() inside useEffect like so

useEffect(() => {
  if (!isPortOpen) {
    connect();
  }
}, [isPortOpen])

CodePudding user response:

This doesn't answer your question but maybe you will find this post helpful nonetheless.

I haven't seen styled-components before and thought it was funny I could replace a 33,500 Bytes package with 10 dependencies in a single line of JavaScript, styled below.

function styled(Tag, style = {}) {
  return props => <Tag style={style} {...props} />
}

const Foo = styled("div", {backgroundColor: "blue", padding: "1rem" })
const Big = styled("button", {fontSize: "3rem"})

function App() {
  return <Foo>
    <Big onClick={e => console.log("hello")}>
      Hello
    </Big>
  </Foo>
}

ReactDOM.render(<App/>, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.development.js"></script>
<div id="app"></div>

  • Related