I'm working on a component for a chrome extension that needs to listen for messages passed from other parts of the extension and update the display based on those messages.
A simplified version of what I thought should work is below but it does not do what I would expect.
This snippet successfully receives the message and calls the increment function, but never increments passed 1, with the log just repeating Incrementing 0 -> 1
// app.jsx
import React from 'react'
import { createRoot } from "react-dom/client";
function App() {
const [ counter, setCounter ] = React.useState(0)
function increment() {
console.log("Incrementing ", counter, " -> ", (counter + 1));
setCounter(counter + 1)
}
React.useEffect(() => {
chrome.runtime.onMessage.addListener(message => increment())
}, [ ])
return <h1>Counter: {counter}</h1>
}
createRoot(document.getElementById('root')).render(<App />)
After some experimentation I discovered that if I set an intermediary value, and then update the counter via a seperate effect, as in the snippet below, it works exactly as I would expect.
// app.jsx
import React from 'react'
import { createRoot } from "react-dom/client";
function App() {
const [ counter, setCounter ] = React.useState(0)
const [ message, setMessage ] = React.useState()
function increment() {
console.log("Incrementing ", counter, " -> ", (counter + 1));
setCounter(counter + 1)
}
React.useEffect(() => {
chrome.runtime.onMessage.addListener(message => setMessage(message))
}, [ ])
React.useEffect(() => increment(), [ message ])
return <h1>Counter: {counter}</h1>
}
createRoot(document.getElementById('root')).render(<App />)
The message sending is identical in both versions and working as expected in both cases. It is just a sendMessage call from a content script.
// contentScript.js
chrome.runtime.sendMessage({action: 'INCREMENT' })
This solution is acceptable, but I'm having trouble understanding why the second works as expected when the first does not.
Is anyone able to explain this behaviour?