change react state in chrome.runtime.onConnect.addListener

175 views
Skip to first unread message

Kerstin Bachmann

unread,
Oct 4, 2023, 3:38:55 PM10/4/23
to Chromium Extensions
Hello every one,

I am very confused, and I hope someone can help me. I'm trying to change the previous State by adding an element to the array. Once the first message is received, the message will be added to the array. After this point, the behavior gets crazy.
In future rounds stateA will stay an empty array in pos B and consequently one line later, the element will not be added to previous State instead the State will be set with an element added to an empty array.
The state in pos A reflects the real State, not an empty array. Can someone explain this to me? Is stateA frozen in time, where the Listener is added? And will always be the initial State never the actual State? Has someone an Idea how to bypass this behavior?


function Info() {
  const [stateA, setStateA] = useState([])
 
  console.log(stateA) // pos A
  chrome.runtime.onConnect.addListener((port) => {
    port.onMessage.addListener(newInfo => {
      console.log(stateA); // pos B
      setStateA([...stateA, newInfo]);
    })
  });

Kerstin Bachmann

unread,
Oct 4, 2023, 4:15:26 PM10/4/23
to Chromium Extensions, Kerstin Bachmann
Ok, I found a way to bypass this Situation.
I think this is more a compromise solution. Nevertheless, here is the idea:
Receive the "newInfo" in a parent Component. Put it there in a state. Pass the state in a prop to the children. Use useEffect() to add the prop to the stateA array.
I hope someone smarter has a more eloquent idea :)

Simeon Vincent

unread,
Oct 4, 2023, 4:21:25 PM10/4/23
to Kerstin Bachmann, Chromium Extensions
I'm not a React expert, but I think the problem is that you're using useState instead of useEffect. The useState hook allows you to update a functional component's state. For example, in this Counter component taken from the useState docs, the handleClick function is invoked from the view layer. This enables react to see and react to the invocation of that function. 

default function Counter() {
const [count, setCount] = useState(0);

function handleClick() {
setCount(count + 1);
}

return (
<button onClick={handleClick}>
You pressed me {count} times
</button>
);
}

The useEffect hook, on the other hand, "is a React Hook that lets you synchronize a component with an external system." In other words, it enables your React app to receive updates such as custom DOM events or extension events.

In addition to digging into useEffect, I'd also suggest checking out custom hooks. That may provide a more robust way to integrate with extension message APIs than writing a bunch of similar useEffect calls across your components.

Simeon - @dotproto


--
You received this message because you are subscribed to the Google Groups "Chromium Extensions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-extens...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-extensions/980d3d2d-750e-48c6-8852-b69cb445c548n%40chromium.org.

Kerstin Bachmann

unread,
Oct 5, 2023, 2:48:24 PM10/5/23
to Chromium Extensions, Simeon Vincent, Chromium Extensions, Kerstin Bachmann
Hello Simeon,

thank you for your answer and your time.
I don't think useState is the problem, here. The Problem is already in the Code Block:

chrome.runtime.onConnect.addListener((port) => {
    port.onMessage.addListener(newInfo => {
      console.log(stateA); // pos B
      setStateA([...stateA, newInfo]);
    })
  });

Lets call this Block X.
For example can console.log at pos B also not see an updated stateA (by the way, also other states which do completly different things will stay the initialized state inside Block X). So it seems that Block X can see variables from outside, but only the initiated states, not updated ones. So you can not update a state inside Block X, where you need the previous state as a basis for.

The reason why useEffect is sometime used for external sources is because of side effects. For example, if you fetch information from an API and put it in a state. The can produce an infinity loop of re-rendering:

fetch -> state -> re-render -> fetch -> state -> ...

Here we wouldn't have such a problem. Because Block X would only be called if a newInfo is received.

The other problem with using useEffect here is we need a dependecy. The dependency should be a state inside the component or a prop which will be passed from a perent to this component. Here the incomming newInfo should be the trigger. What we can do is the following: put this new Info in an additional state, and this state is the trigger for the useEffect to enrich the array.
This is more or less the solution I explained in the second post.

Conclusion:
- I think the rules corresponding to react state inside Block X are not intuitive. And with my current knowledge, I can't see the reason behind this.
- To bypass this situation we have to create an unnecessary additional state and with this unnecessary re-renderings.

Because of this, the question is still to find a more eloquent way to handle this situation.

Best wishes
Reply all
Reply to author
Forward
0 new messages