1

I am writing a component for my pinball game. I have to setState inside the function triggered by the 'on contact' listener. But state is not updating. Is there another design that i can try?

function Pinball(){
  const[score, setScore] = useState(0);

  useEffect(()=>{
      //... here I set physics environment and have variable world
      world.on('begin-contact',  function(contact){
             //...
             setScore( score + 1 );
       })
  }, []);

  return (<span>{score}</span>);

}
15
  • worst case scenario, wrap it with a promise, resolve the value you need from the callback, await the promise, then setState. Commented Dec 30, 2019 at 19:39
  • 5
    "I have read that we can not use setState inside nested functions." This simply isn't correct. Also, you're using a state hook, not setState. Commented Dec 30, 2019 at 19:41
  • 1
    what is world? did you check to see if the callback is executed on begin-contact event? Commented Dec 30, 2019 at 19:51
  • 1
    For the record, I think what you read about using the state hook was referring to calling useState. That should only be done in the functional component directly, not in a nested function. But then you can call setScore from anywhere in the enclosing scope Commented Dec 30, 2019 at 20:01
  • 2
    Don't forget to remove the event listener on component unmount or you'll end up with a memory leak pretty quickly. You're allowed to use the state update function anywhere you'd like; it's only the initial invocation of a hook that has rules on where it can and cannot be called. Commented Dec 30, 2019 at 20:02

1 Answer 1

1

You can use useCallback for the callback handler, and useEffect to set the 'begin-contact' event listener. You must pass dependencies in as so:

function Pinball(){
  const[score, setScore] = useState(0);
  const handleBeginContact = useCallback(function(contact){
    setScore( score + 1 );
  }), [score] );
  useEffect(()=>{
    world.on('begin-contact',  handleBeginContact);
  }, [world])

  return <span>{score}</span>;
}

You can simplify setScore event further like this: setScore(score => score+1) Then you don't have to pass the score dependency in.

Additionally, as Dan Pantry said in a comment, you will want to return a function inside the useEffect which unregisters the 'begin-contact' event listener, for example return () => world.off('begin-contact'), but that will depend on your implementation of world.

Sign up to request clarification or add additional context in comments.

2 Comments

No, only if world changes.
using setScore(score => score+1) instead of setScore(score + 1 ) solved the problem even without using the useCallback hook. I don't know the reason but it worked.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.