Writer Plugin Component

A Component is a way to visualize data, handle input, and make your dreams come true. When registering a Component in a plugin's Package file, its possible to couple it with a Node, but it's also completely possible to create a Component independently of a Node. When creating sidebar plugins, it's preferred to have one of these independent Components, and handle any NewsItem manipulation from within your plugin classes.

The render function

The most important function in a Component is its render function. This function should return a VirtualElement created by using the helper function $$. The $$ function takes either a tag-name as input, to create a DOM-element, or a Substance Component, which in turn calls that Component's render function and returns the result.

Creating a simple HTML element:

import {Component} from 'substance'

class MyComponent extends Component {
    render($$) {
        return $$('p').append('Hello World!')
    }
}

Creating a rendered Component:

import {Component} from 'substance'
import {AnotherComponent} from './AnotherComponent'

class MyComponent extends Component {
    render($$) {
        const message = 'Hello World'
        return $$(AnotherComponent, {
            message
        })
    }
}

The context Object

In a class extending Component, the property context exists which exposes some useful services and functionality. In any method in your component, fetch context by using this.context.

Property Description
state Gets the current state of the article, contains an object with boolean properties changed, invalid, saving
configurator Gets the current configurator
appStorage Allows access to the localStorage driven app storage
editorSession Allows access to editorSession instance
labelProvider Gets the labelprovider registered in the Configurator
api Gets the Api instance, see Api documentation
componentRegistry Gets the componentRegistry registered in the Configurator
services.conceptService Gets the instance of conceptService, see Concept interaction

Component State and Props

Some excerpts from Substance source code:

props are provided by a parent component. An initial set of properties is provided via constructor.

state is a set of flags and values which are used to control how the component gets rendered given the current props. Using extendState the component can change its internal state, which leads to a rerendering if the state changes.

Both props and state affects the rerendering of a Component. To keep it simple, props should only be receieved, and state should only be used internally. There are exceptions to this, but it's easier to manage if used in this way. It's possible for Components to communicate using Events.

Setting Initial State

When a Component renders for the first time, it might not have its entire state fully loaded, and it's good practice to let the Component know what its state looks like for its first render. This is a simple task, as Substance's Component class contains the function getInitialState which can be overridden in your own Components.

Component Initial State Example:

import {Component} from 'substance'

class MyComponent extends Component {

    getInitialState() {
        return {
            importantMessage: 'Initial Message'
        }
    }

    render($$) {
        const {importantMessage} = this.state
        return $$('p').append(importantMessage) // importantMessage is ensured to exist on first render
    }
}

Sending Props to a Child Component

In order to structure components, it's possible to render a Component within a different Component, and supply that child Component with props from the parent. This is an easy way to avoid bloated Components.

Child Component Example:

// MyParentComponent.js

import {Component} from 'substance'

class MyParentComponent extends Component {

    render($$) {
        const myListOfThings = ['hello', 'world', 'foo', 'bar']

        const renderedListOfThings = myListOfThings.map((thing) => {
            return $$(MyChildComponent, {
                item: thing // This object is assigned to `this.props` in MyChildComponent
            })
        })

        return $$('ul').addClass('my-list').append(
            renderedListOfThings
        )
    }

}

export {MyParentComponent}

// MyChildComponent.js

import {Component} from 'substance'

class MyChildComponent extends Component {

    render($$) {
        const {item} = this.props

        return $$('li').append(
            $$('b').append(item)
        )
    }    

}

export {MyChildComponent}

Output HTML:

<ul class="my-list">
    <li><b>hello</b></li>
    <li><b>world</b></li>
    <li><b>foo</b></li>
    <li><b>bar</b></li>
</ul>

Writing a Content Component with Node connection

A Content Component is a Component which is rendered in the Writer's content area, such as text style, content part, image, etc. These Components are coupled with a Node in order to handle the automatic import/export from, and to, NewsML. When writing a Content Component, it's safe to assume that the Component's props contain a property node, which references an instance of the Component's coupled Node class.

Component Lifecycle

A Component class has a number of different lifecycle hooks which can be overridden and used to subscribe/unsubscribe from Events, manipulate props/state before they are set, or handle cleanup before the Component is removed from Writer's content area.

Function Name Trigger
didMount() Before render
didUpdate() After render
dispose() When DOMelement is removed
willReceiveProps(newProps) When props update, useful for also changing state which depends on props
willUpdateState(newState) Before state updates

Override a Component Lifecycle Hook

import {Component} from 'substance'
import {AnotherComponent} from './AnotherComponent'

class MyComponent extends Component {

    didMount() {
        // Setup the Component before its first render
    }

    render($$) {
        const message = 'Hello World'
        return $$(AnotherComponent, {
            message
        })
    }
}

export {MyComponent}

results matching ""

    No results matching ""