0

I'm have to match a note to an incoming note detected by the microphone. I'm using a fft to detect the pitch and matches that to the next closest note. It triggers the handleNoteEvent about 10x/s. The problem is since setNote is async the if statement == true multiple times, until setState has finished setting the value which subsequently causes the app to rerender multiple times. How can I wait until setState has finished while using react hooks? (currentNote is use by multiple children)

EDIT: setState with hooks doesn't seem to return a promise or take a callback if I understand the docs correctly

EDIT 2: I think I have to clarify my issue: I somehow need to ignore incoming events after the if becomes true, until setState has finished setting currentNote to a new note object.

function App() {
const [currentNote, setNote] = useState(new Note());

//Event handler that gets the event from the fft tuner multiple times a second
const handleNoteEvent = (fftNote) => {
    if (currentNote == fftNote)) {
      console.log('match');
      nextNote();
    }
//The problem here is the nextNote() is fired multiple times since setNote is async. How can I ignore all incoming events while setNote is not finished?
const nextNote = () => {setNote(new Note())};
...
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Nils
  • 13
  • 5
  • Does this answer your question? [React.js, wait for setState to finish before triggering a function?](https://stackoverflow.com/questions/37401635/react-js-wait-for-setstate-to-finish-before-triggering-a-function) – Rojo Feb 13 '21 at 16:59
  • This is not working for a functional component. setState in functional component does not accept the second argument. – Quentin Grisel Feb 13 '21 at 17:05
  • How did `setNote` become async? AFAIK, `setNote` is sync because it's React useState hook. – glinda93 Feb 13 '21 at 17:17
  • Well the value of current note is only changed after about 500ms, thats why I figured it was async – Nils Feb 13 '21 at 17:18
  • Is it because `Note` constructor is expensive? – glinda93 Feb 13 '21 at 17:20
  • Yes the note class does some calculation, but nothing which should take too long. Might explain the delay too though – Nils Feb 13 '21 at 17:40

2 Answers2

0

You will have to handle what happens after the setNote in another useEffect. Something like :

React.useEffect(() => {
  // Triggered only once when your component mounts (= componentDidMount in class component)
}, []);

React.useEffect(() => {
  // It is only triggered when not changes.
  // Do your things after setting notes here.
  // Be careful about looping. If you set note here, you'll need to check the value as you did in your exemple to prevent updating note indefinitely
}, [note]);

You are not limited to one useEffect or one hook in general. Split your tasks with multiple useState and useEffect if needed.

Quentin Grisel
  • 4,794
  • 1
  • 10
  • 15
  • Oh thanks I'll give that a go asap. I tried that already but had the recursion problem you mentioned. If I check the state there it has already finished setting? – Nils Feb 13 '21 at 17:16
  • Yes, this is the purpose of the dependencies in a useEffect, it is triggered AFTER the deps changes. – Quentin Grisel Feb 13 '21 at 17:19
  • Well, unfortunatly that still leaves me with the same problem, since I the event is triggered multiple times anyways and I don't know how to ignore it – Nils Feb 13 '21 at 17:42
  • @Nils There might be something wrong with your check to differentiate two notes then. If there is no `setNote` triggerd (So if currentNote !== ffNote) then the useEffect with currentNote as dependency will not be triggered. – Quentin Grisel Feb 13 '21 at 17:52
  • The problem with the useEffect is, I don't trigger any function after the note is set. Is just passes the currentNote to a child which then draws a new note. It should just ignore the notes the fft inputs until everything has finished rendering – Nils Feb 13 '21 at 17:58
0

Well what I have now works as it should while implementing the useEffect on note change. It seems to be quite a dirty solution and I would be grateful if anyone could tell me how to do it more clean:

const [currentNote, setNote] = useState(new Note());
var waiting = false;
const handleNoteEvent = (receivedNote) => {
    if ((currentNote == receivedNote) && !waiting) {
      waiting = true;
      setNote(new Note());
    }
  };
  useEffect(() => {
    waiting = false;
  }, [currentNote]);
Nils
  • 13
  • 5