Plugin View Lifecycle
- entry point: App (Private)
IDE Mode
Render plugin views inside of VSCode. See IDE Mode for more details.
From Init View
Go to text ā
Browser Mode - Startup
Render plugin views inside of chrome. See Browser Mode for more details.
Lifecycle
- compile: starting webpack dev server, creating the index.html page based on
REACT_APP_VIEW_NAME
- renderIndex: dynamically loading
REACT_APP_VIEW_NAME
component - renderDendronVsCodeApp: rendering of
REACT_APP_VIEW_NAME
component
stateDiagram-v2
[*] --> yarnStart
yarnStart --> compile
state compile {
[*] --> buildIndex
buildIndex--> start.js
start.js-->startWebpackWithDevServer
startWebpackWithDevServer-->compileSaasStyles
compileSaasStyles-->openBrowserToIndexPage
openBrowserToIndexPage --> [*]
}
compile --> render
state render {
state renderIndex {
[*] --> loadIndex.js
loadIndex.js --> requireView
requireView --> renderOnDOM
renderOnDOM --> renderWithDendronApp
renderWithDendronApp --> createDendronVsCodeApp
createDendronVsCodeApp --> [*]
}
renderIndex --> renderDendronVsCodeApp
state renderDendronVsCodeApp {
[*] --> postVSCodeMessage
[*] --> useVSCodeMessage
[*] --> renderCustomComponent
useVSCodeMessage --> renderCustomComponent
}
}
The following discusses each phase in more detail with pseudocode
compile
- User runs
yarn:start
This runs the following tasks:"yarn build:index && node scripts/start.js"
- NOTE:
build:index
generates theindex.html
file that is used to load the plugin. More details in Build Index
- NOTE:
load webpack
-
Webpack loads ../packages/dendron-plugin-views/scripts/start.js (Private)
```ts config = configFactory createCompiler(config) createDevServerConfig prepareProxy openBrowser ``` - config: [[../packages/dendron-plugin-views/config/webpack.config.js]] ```ts mode: isEnvProduction ? "production" : "development" entry: ( mode = development? [ paths.appIndexJs ] : ... ) ```
Render
From Render
Go to text ā
renderIndex
- Import index
VIEW_NAME = process.env["REACT_APP_VIEW_NAME"] || "";
...
View = require(VIEW_NAME)
renderDendronVsCodeApp
-
Import a component and wrap it with its own DOM renderer
- NOTE: the following uses
src/views/DendronNotePageView.tsx
as an example
import DendronNotePage from "../components/DendronNotePage"; renderOnDOM(DendronNotePage);
- NOTE: the following uses
-
renderOnDOM
- this is a helper: wraps the component with the parent
DendronApp
container and renders it usingReactDOM
renderOnDOM(Component) { ReactDOM.render( {renderWithDendronApp(Component)} ) }
- this is a helper: wraps the component with the parent
-
renderWithDendronApp
- wraps the component with
DendronVSCodeApp
DendronApp { <Provider> <DendronVSCodeApp /> </Provider> }
- wraps the component with
-
DendronVSCodeApp
DendronVSCodeApp { ctx = "DendronVSCodeApp" log "enter", workspace // see [[useEngine|dendron://dendron.docs/pkg.dendron-plugin-views.arch.lifecycle#useengine]] useEngine useEffect { // tell vscode that client has loaded postVSCodeMessage { type: INIT, source: webClient } } // listen to vscode messages // on INIT, vscode will send over all current notes and schemas useVSCodeMessage { ... } }
renderCustomComponent
-
src/components/DendronNotePage.tsx
DendronNotePage { // this renders the note page // noteProps is obtained from vscode and passed in via redux useRenderedNoteBody(noteProps, noteId) { renderedNoteContentHash = useRef if noteProps.contentHash != renderedNoteContentHash { engineSlice.renderNote noteId } } ... }
render
renderIndex
- Import index
VIEW_NAME = process.env["REACT_APP_VIEW_NAME"] || "";
...
View = require(VIEW_NAME)
renderDendronVsCodeApp
-
Import a component and wrap it with its own DOM renderer
- NOTE: the following uses
src/views/DendronNotePageView.tsx
as an example
import DendronNotePage from "../components/DendronNotePage"; renderOnDOM(DendronNotePage);
- NOTE: the following uses
-
renderOnDOM
- this is a helper: wraps the component with the parent
DendronApp
container and renders it usingReactDOM
renderOnDOM(Component) { ReactDOM.render( {renderWithDendronApp(Component)} ) }
- this is a helper: wraps the component with the parent
-
renderWithDendronApp
- wraps the component with
DendronVSCodeApp
DendronApp { <Provider> <DendronVSCodeApp /> </Provider> }
- wraps the component with
-
DendronVSCodeApp
DendronVSCodeApp { ctx = "DendronVSCodeApp" log "enter", workspace // see [[useEngine|dendron://dendron.docs/pkg.dendron-plugin-views.arch.lifecycle#useengine]] useEngine useEffect { // tell vscode that client has loaded postVSCodeMessage { type: INIT, source: webClient } } // listen to vscode messages // on INIT, vscode will send over all current notes and schemas useVSCodeMessage { ... } }
renderCustomComponent
-
src/components/DendronNotePage.tsx
DendronNotePage { // this renders the note page // noteProps is obtained from vscode and passed in via redux useRenderedNoteBody(noteProps, noteId) { renderedNoteContentHash = useRef if noteProps.contentHash != renderedNoteContentHash { engineSlice.renderNote noteId } } ... }
Change Active Editor
When the active editor changes, the useVSCodeMessage
hook in DendronVSCodeApp (Private) will update the state with note(s) that have changed
useVSCodeMessage(msg) {
ctx = "useVSCodeMsg"
switch(msg) {
case ON_DID_CHANGE_ACTIVE_TEXT_EDITOR {
note, sync := msg
log "onDidChangeActiveTextEditor"
// update all notes
if sync {
log "syncEngine:pre"
dispatch(initNotes)
}
// update one note
if (syncChangedNote && note)
log "syncNote:pre"
ideDispatch(engineSlice.syncNote)
...
dispatch(setNoteActive(note))
}
}
}
Common
Some utilities that are commonly used
useEngine
- loc: common-frontend/src/features/engine/hooks.ts
- desc: initialize engine if its not initialized
useEngine(engineState) {
useEffect {
if !hasInitialized(engineState)
// see [[initNotes|dendron://dendron.docs/pkg.dendron-plugin-views.arch.lifecycle#initnotes]]
dispatch(initNotes)
}
}
initNotes
- loc: common-frontend/src/features/engine/slice.ts
- desc: initialize notes for redux engine
// sideEffect, when initNotes is dispatched, state is set to Pending
effect(state, requestId) {
when initNotes.pending {
if (state.loading = "idle") {
state.loading = LoadingStatus.PENDING;
state.currentRequestId = meta.requestId;
}
}
}
initNotes {
api = DendronApiV2.new
resp = api.workspaceSync
setFromInit(resp) {
// set all variables
state.notes = notes;
state.wsRoot = wsRoot;
state.schemas = schemas;
state.vaults = vaults;
state.config = config;
}
}
// side effect, after notes are set, state is set back to idle
effect(state, requestId) {
when initNotes.fulfilled {
if (state = "idle" && state.currentRequestId === requestId) {
state.loading = "idle"
delete state.currentRequestId
}
}
}
Children
Backlinks