Using Redux in MV3 Chrome Extensions

Translation Note : The original article was written before MV3 was known. Nevertheless, it is completely relevant for MV3 extensions (at least for the moment). Therefore, I decided to slightly change its name, adding the mention of "MV3", which does not contradict the content at all. If anyone is not in the know: MV3 - the new format / standard for Chrome extensions, should be introduced in January 2021.







This article, aimed at experienced web developers, examines (and solves) the problem of using Redux in the so-called. event-driven (event-driven) extensions of Chrome.







Specificity of event-driven extensions



The event-driven extension model first appeared in Chrome 22 in 2012. In this model, the background script of the extension (if any) is loaded / executed only when needed (mostly in response to events) and unloaded from memory when it does nothing.







Chrome developer documentation strongly advises using the event-driven model for all new extensions, and migrating for existing extensions using the persistent model . There is, however, one exception ( in MV3 it is no longer relevant, including therefore the transition to a new model in MV3 is mandatory). But it looks like many extensions still use the persistent model, even though they might be event-driven. Of course, many of them were first released before the event-driven model became known. And now their authors simply have no incentive to switch to a new model. On the one hand, this is (yet) optional, but on the other hand, it means the need for many changes, not only in the background script, but also in other extension components. In addition, many use a cross-browser approach when developing.by collecting ready-made extensions for different browsers from the same source code. The event-driven model is currently supported only in Chrome and is significantly different from the persistent model supported by other browsers. Naturally, this complicates cross-browser development.







However, some of the relatively newer Chrome extensions also use a persistent model, and may be event-driven. Ultimately, the reason is the same as in the case of migration: there are significant differences between event-driven and persistent models, which are mainly expressed in the way we manage the state of the extension.







The Redux problem



— , -. - ( .. ), , . Redux.







Redux — , , . Redux .







, " " — Redux, . ( ), . , Webext Redux, . - - - . .







(persistent) , , . (, ) , . , , Webext Redux.













- , , , , . , , .. ( ). .









, chrome.storage



/ / . ( ) , chrome.storage



, API , .













API chrome.storage



, — . — , , " ".







, , -, . , , .







— API chrome.storage



Redux, Redux. , chrome.storage



, . , Redux - . - chrome.storage



Redux, , Redux chrome.storage



).







— Redux- chrome.storage



, chrome.storage



Redux. API chrome.storage



Redux, (store) Redux. createStore



Store



( Redux). :











, , Store



. ReduxedStorage



.







getState



subscribe



, .. chrome.storage



: get



onChanged



. , Store



, . , get



chrome.storage



ReduxedStorage



, onChanged



, . . getState



. subscribe



: - , onChanged



.







getState



subscribe



, chrome.storage



Store.dispatch



. set



, Redux, Redux , , dispatch



. - dispatch



ReduxedStorage



. . Redux , , Redux. , .







, , - . , , . , dispatch



, , "" createStore



, "" Store.dispatch



, dispatch



. , , , chrome.storage



, . chrome.storage.onChanged



, .







: chrome.storage:get



, . chrome.storage:get



, ( ). , init



, , , chrome.storage:get



. init



Redux, , , chrome.storage



.







ReduxedStorage



:









: chrome.storage



(this.key



), () chrome.storage.onChanged



, chrome.storage:get



. , , .. chrome.storage



.







, , - , this.state



, chrome.storage:set



, . . Redux dispatch



this.state



, , .. this.state



. , . 2- dispatch



this.state



, - chrome.storage:set



. , .







, dispatch



, Redux. ( 100 ), . — . dispatch



:







dispatch


, , . Redux. , Redux, middleware, Redux Thunk. Redux Thunk , , . :









delayAddTodo



'ADD_TODO'



1 .







dispatch



, this.buffStore.getState



this.buffStore.subscribe



. this.buffStore.subscribe



1 dispatch



, this.buffStore



null



( 100 dispatch



). dispatch



, .. , subscribe



.







, , .. , , Redux. , — , - , delayAddTodo



. , , Redux dispatch



. , this.buffStore



, , lastStore



. , this.buffStore



, lastStore



subscribe



. , subscribe



lastStore



, this.buffStore



, " "). subscribe



, / lastStore



, .







, , ..:







  • this.areaName



    , this.key



    / .
  • , API chrome.storage



    , , WrappedStorage



    .


, :









Its use is similar to that of the original Redux, except that in our interface the store creator is wrapped in a function and executed asynchronously, returning a promise instead of the created store.







Standard usage of the interface looks like this:







Standard use


Also, with syntax async/await



available starting in ES 2017, this interface can be used like this:







Advanced use


The source code is available on Github.







Also this interface is available as a package on NPM:







npm install reduxed-chrome-storage
      
      






All Articles