SlideShare ist ein Scribd-Unternehmen logo
1 von 131
Downloaden Sie, um offline zu lesen
React for Reuse
Creating reusable UI Components with React
MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN
Introduction
About this Talk
Me
Confluence
Platform
Ecosystem
Me
Confluence
Platform
Ecosystem
Confluence
Platform
Me
Confluence
Platform
Ecosystem
Core APIs
Confluence Platform
Ecosystem
Front End Platform
Me
Confluence
Platform
Ecosystem
Ecosystem
Confluence Connect
Add-ons
Partnerships
Client Side
We use extended JavaScript
on the client side to create
the UI Components.
Server Side
In this example we use server
side JavaScript by using
NodeJs.
JavaScript Everywhere
Introduction
Add-on
Stack
Introduction
Add-on
Stack Express
(ACE)Atlassian Connect Express
Introduction
Add-on
Stack Express
(ACE)Atlassian Connect Express
Introduction
Add-on
Stack
ExpressACE
Introduction
Add-on
Stack
Introduction
Add-on
Stack
Static HTML
Introduction
Add-on
Stack
Sample Project
Confluence Issues Add-on
bitbucket.org/mjensen/confluence-issues-addon
bit.ly/2oqVGyb
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Issues
Add-On
Overview
Issue List
Issue Macro
Getting Started
Setting up the Project
bitbucket.org/atlassian/atlassian-connect-express
bit.ly/TTpDn1
Getting
Started
ACE
Project
Create the project
Use atlas-connect to create a project using the
confluence template.
confluence-issues-addon
public
routes
views
src
Components
A React Primer
Components
Functional
Components are Functions.
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Functional
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Functional
Classes
State
Components
function Clock(props) {

return <span>{moment().format()}</span>;

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Use a function to define a Component
Functional
Classes
State
f(x) Functional Components
Simple
Defining a component as a
function is very simple.
Stateless
Components defined as
functions have no local state.
Components are Classes.
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
class Clock extends React.Component {

render() {

return <span>{moment().format()}</span>;

}

}
ReactDOM.render(

<Clock />,

document.getElementById('root')

);
Components
Functional
Classes
State
Use a class to define a Component
Components as Classes
Classes
Classes can benefit from
inheritance, composition and
other object orientation
strategies.
Stateful
Components defined as classes
can define their own state and
lifecycle.
Components have State.
Components
Functional
Classes
State
Components
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
Functional
Classes
State
Components
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
Functional
Classes
State
Components
Functional
Classes
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = { date: moment() };

}


render() {

return (
<span>
{this.state.date.format()}
</span>
);

}

}
State
Class components can have state.
State Management
Component State
React provides simple state
management for each
component.
Centralised State
Redux is a centralised state
management system, often
simplifying your components
dramatically.
Components have a Lifecycle.
Components
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
Components
class Clock extends React.Component {
// ...
componentDidMount() {

// called after the component is added to the DOM

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

// called before the component is removed from the DOM

clearInterval(this.timerID);

}



tick() {

this.setState({ date: moment() });
} 

// ...

}
Lifecycle
Props
The lifecycle methods update the state.
Props vs State.
Components
Lifecycle
Props
Components
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
// src/components/Clock.js
class Clock extends React.Component {
render() {

return (
<span>{this.state.date.format(this.props.dateFormat)}</span>
);

}

}
Clock.defaultProps = {

"dateFormat": 'MMMM Do YYYY, h:mm:ss'

};
// clock.js
ReactDOM.render(

<Clock />,

document.getElementById('clock')

);
ReactDOM.render(

<Clock dateFormat='h:mm:ss'/>,

document.getElementById('clock')

);
Components
Lifecycle
Props
Props are set when created and are immutable.
Component Types
Container
Fetches the application data
and composes the User
Interface out of Presentation
components.
Presentation
Takes the data from a Container
component and presents it to
the user.
confluence-issues-addon
public
routes
views
src
components
containers
Project Components
Components of Confluence Issues Add-on
IssuesAppContainer
Decides what to display in the page body.
IssuesList
Displays the column headings and
an IssueListItem for each row.
IssuesListItem
Represents a row in the list of issues.
confluence-issues-addon
src
components
containers
confluence-issues-addon
src
components
containers
IssueAppContainer.js
list
IssueList.js
IssueListLineItem.js
IssueAppContainer
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
loadIssues(spaceKey) {

return (callback) => {

AP.require('request', function (request) {

request({

url: '/rest/api/content',

data: { … },

success: (data) => {

let issues = JSON.parse(data)
.results
.map(Issues.contentToIssue);
callback(localIssues);

}

});

});

}

}
Load Issues
loadIssues
Reusable function to load issues.
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueAppContainer extends React.Component {


componentDidMount() {

this.props.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}

render() {

let createIssueClick = …;

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueAppEmpty
createIssueClick={createIssueClick}/>)

} else {

return (<IssueAppPopulated
createIssueClick={createIssueClick}

issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
Use the state to decide what to render.
IssueList
[

{

"title": "Build…",

"type": "task",

"status": "new",
…

},
…

]
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Issue App
Components
Issue App
Issue List
Issue Macro


export default class IssueList extends React.Component {

render() {

let issues = this.props.issues;

let fields = …;



return (

<table className="list-of-issues">

<IssueListHeadings fields={fields}/>

<tbody>

{issues.map((issue) => {

return (

<IssueLineItem
key={issue.key}
fields={fields}
issue={issue}/>)

})}

</tbody>

</table>

);

}

}
Presentation components have no state.
Issue App
Components
Issue App
Issue List
Issue Macro
Macro
This macro shows a list of issues.
Issue App
Issue List
Issue Macro
Issue App
Components
IssueMacroContainer
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Issue App
Components
Issue App
Issue List
Issue Macro
export default class IssueMacroContainer extends React.Component {



componentDidMount() {

this.loadIssues((issues) => {

this.setState({issues: issues, loading: false})

});

}



render() {

if (this.state.loading) {

return (<Loading/>);

} else {

let emptyState = (this.state.issues.length === 0);

if (emptyState) {

return (<IssueMacroEmpty/>)

} else {

return (<IssueMacroPopulated
issues={this.state.issues}/>)

}

}

}

}
Same concept as IssueAppContainer.
Entry Points
Mapping Extensions to Components
MacroFull Page
Dialog
App.js
Macro.js
NewIssueDialog.js
confluence-issues-addon
src
components
containers
entry-points
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
import IssueMacroContainer from '../containers/IssueMacroContainer';



let spaceKey = queryString.parse(location.search)["spaceKey"];



ReactDOM.render(<IssueMacroContainer

loadIssues={Issues.loadIssues(spaceKey)}/>,
document.getElementById("list-issues"));
Entry Point
Import
Context
Render
An entry point will render the top component.
webpack takes modules with dependencies and
generates static assets representing those modules.
http://webpack.github.io/
confluence-issues-addon
webpack.config.js
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
module.exports = {

resolve: {root: [__dirname + path.sep + 'assets']},

devtool: 'source-map',

entry:{

app: './src/entry-points/App.js',

dialog: './src/entry-points/NewIssueDialog.js',

macro: './src/entry-points/Macro.js',

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ … ]

}

};
Webpack
Configuration
Config
Entry
Output
Webpack will bundle your components into static
files that a browser will understand.
confluence-issues-addon
public
js
macro.js
{{!< layout}}

<div id="list-issues"/>

<script src="/js/macro.js"></script>
Recap
Component
State
Entry
HTML
Building
Using NPM to build your App
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
{

"name": "confluence-issues-addon",

"version": "0.0.1",

"private": true,

"scripts": {

"start": "node app.js",

"bundle": "webpack",

"watch": "webpack --watch",

"prestart": "webpack"

},

"dependencies": { … },

"devDependencies": {

"babel-core": "^6.9.0",

"babel-loader": "^6.2.4",

"babel-preset-es2015": "^6.9.0",

"babel-preset-react": "^6.5.0",

"webpack": "^1.13.0"

}

}
Building
Package
Bundle
Start
Babel is used to transpile the extended JavaScript.
Transpiling
ES2015 Extensions, modules,
dependencies, etc
Standalone files, compatible
with browsers, etc
Transpiling
Run a the webpack script
Use npm run bundle to run the webpack script we
defined in the package.json file.
Building
Package
Bundle
Start
Building
Package
Bundle
Start
Run a the webpack script
Use npm run bundle to run the webpack script we
defined in the package.json file.
Create the static JavaScript files
These files are self contained and browser compatible
JavaScript resources.
Building
Package
Bundle
Start
Client app.js
Refers to the application
entry point component
Server app.js
Default name for the Node.js
server entry point.
Two Apps
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Building
Package
Bundle
Start
Create the project
Use npm start to start the node app, automatically
transpiling the client components.
Node JS
Uses NPM to package both server and client side
modules.
Manually Package
You can still use NPM to bundle your client side
components
Dev Loop
Restart your add-on service and call webpack on
each change.
Recap
Quick recap of what we have learnt so far
Recap
Component
State
Entry
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
class Clock extends React.Component {

constructor(props) {

super(props);

this.state = {date: moment()};

}



componentDidMount() {

this.timerID = setInterval(() => this.tick(), 1000);

}



componentWillUnmount() {

clearInterval(this.timerID);

}



tick() {

this.setState({date: moment()});

}



render() {

return (<span>{this.state.date.format(this.props.dateFormat)}</span>);

}

}



Clock.defaultProps = {

'dateFormat': 'MMMM Do YYYY, h:mm:ss'

};



export default Clock;
Recap
Component
State
Entry
Building
ReactDOM.render(

<Clock dateFormat='MMMM Do YYYY, h:mm:ss'/>,

document.getElementById('clock')

);
Recap
Component
State
Entry
Building
module.exports = {

resolve: { root: [__dirname + path.sep + 'assets'] },

devtool: 'source-map',

entry:{

router: './assets/js/Clock'

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ …]

}

};
Recap
Component
State
Entry
Building
module.exports = {

resolve: { root: [__dirname + path.sep + 'assets'] },

devtool: 'source-map',

entry:{

router: './assets/js/Clock'

},

output: {

path: "./public/js",

filename: '[name].js'

},

plugins: [ … ],

module: {

loaders: [ …]

}

};
Recap
Component
State
Entry
Building
{{!< layout}}

<div id="clock"/>

<script src="/js/clock.js"></script>
Recap
Component
State
Entry
Building
Recap
Component
State
Entry
Building
NPM
Use npm to manage webpack dependencies and
development lifecycle.
Onward
Related Concepts and Advanced Topics
Onward
AtlasKit
Other
Extensions React Components
AtlasKit components are
React Components.
NPM Modules
Define a dependency on the
AtlasKit component and
import it in to your own!
AtlasKit
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Onward
AtlasKit
Other
Extensions
Thank you!
MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN

Weitere ähnliche Inhalte

Was ist angesagt?

Configuring Domino To Be An Ldap Directory And To Use An Ldap Directory
Configuring Domino To Be An Ldap Directory And To Use An Ldap DirectoryConfiguring Domino To Be An Ldap Directory And To Use An Ldap Directory
Configuring Domino To Be An Ldap Directory And To Use An Ldap Directory
Edson Oliveira
 

Was ist angesagt? (20)

Ready to go Mobile? Today's Mobile Landscape: Responsive, Adaptive, Hybrid, a...
Ready to go Mobile? Today's Mobile Landscape: Responsive, Adaptive, Hybrid, a...Ready to go Mobile? Today's Mobile Landscape: Responsive, Adaptive, Hybrid, a...
Ready to go Mobile? Today's Mobile Landscape: Responsive, Adaptive, Hybrid, a...
 
How to monitor your micro-service with Prometheus?
How to monitor your micro-service with Prometheus?How to monitor your micro-service with Prometheus?
How to monitor your micro-service with Prometheus?
 
Redux Toolkit & RTK Query in TypeScript: tips&tricks
Redux Toolkit & RTK Query in TypeScript: tips&tricksRedux Toolkit & RTK Query in TypeScript: tips&tricks
Redux Toolkit & RTK Query in TypeScript: tips&tricks
 
SAP hybris - User Account Management
SAP hybris - User Account ManagementSAP hybris - User Account Management
SAP hybris - User Account Management
 
[오픈소스컨설팅]스카우터엑스 소개
[오픈소스컨설팅]스카우터엑스 소개[오픈소스컨설팅]스카우터엑스 소개
[오픈소스컨설팅]스카우터엑스 소개
 
Composable and streamable Play apps
Composable and streamable Play appsComposable and streamable Play apps
Composable and streamable Play apps
 
MariaDB MaxScale
MariaDB MaxScaleMariaDB MaxScale
MariaDB MaxScale
 
Configuring Domino To Be An Ldap Directory And To Use An Ldap Directory
Configuring Domino To Be An Ldap Directory And To Use An Ldap DirectoryConfiguring Domino To Be An Ldap Directory And To Use An Ldap Directory
Configuring Domino To Be An Ldap Directory And To Use An Ldap Directory
 
[오픈소스컨설팅]Java Performance Tuning
[오픈소스컨설팅]Java Performance Tuning[오픈소스컨설팅]Java Performance Tuning
[오픈소스컨설팅]Java Performance Tuning
 
Spring Framework Petclinic sample application
Spring Framework Petclinic sample applicationSpring Framework Petclinic sample application
Spring Framework Petclinic sample application
 
Latest performance changes by Scylla - Project optimus / Nolimits
Latest performance changes by Scylla - Project optimus / Nolimits Latest performance changes by Scylla - Project optimus / Nolimits
Latest performance changes by Scylla - Project optimus / Nolimits
 
Best practices for MySQL/MariaDB Server/Percona Server High Availability
Best practices for MySQL/MariaDB Server/Percona Server High AvailabilityBest practices for MySQL/MariaDB Server/Percona Server High Availability
Best practices for MySQL/MariaDB Server/Percona Server High Availability
 
Eclipse と Liberty プロファイルで始める Java EE 開発ハンズオン #jjug_ccc #ccc_r51
Eclipse と Liberty プロファイルで始める Java EE 開発ハンズオン #jjug_ccc #ccc_r51Eclipse と Liberty プロファイルで始める Java EE 開発ハンズオン #jjug_ccc #ccc_r51
Eclipse と Liberty プロファイルで始める Java EE 開発ハンズオン #jjug_ccc #ccc_r51
 
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
New features in ProxySQL 2.0 (updated to 2.0.9) by Rene Cannao (ProxySQL)
 
A New CLI for Spring Developer Productivity
A New CLI for Spring Developer ProductivityA New CLI for Spring Developer Productivity
A New CLI for Spring Developer Productivity
 
Top 1000 Java Interview Questions Includes Spring, Hibernate, Microservices, ...
Top 1000 Java Interview Questions Includes Spring, Hibernate, Microservices, ...Top 1000 Java Interview Questions Includes Spring, Hibernate, Microservices, ...
Top 1000 Java Interview Questions Includes Spring, Hibernate, Microservices, ...
 
Hands-on DNSSEC Deployment
Hands-on DNSSEC DeploymentHands-on DNSSEC Deployment
Hands-on DNSSEC Deployment
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBoot
 
Hearts Of Darkness - a Spring DevOps Apocalypse
Hearts Of Darkness - a Spring DevOps ApocalypseHearts Of Darkness - a Spring DevOps Apocalypse
Hearts Of Darkness - a Spring DevOps Apocalypse
 
2021 ee大会-旷视ai产品背后的研发效能工具建设
2021 ee大会-旷视ai产品背后的研发效能工具建设2021 ee大会-旷视ai产品背后的研发效能工具建设
2021 ee大会-旷视ai产品背后的研发效能工具建设
 

Andere mochten auch

Andere mochten auch (20)

Securing Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWTSecuring Your Atlassian Connect Add-on With JWT
Securing Your Atlassian Connect Add-on With JWT
 
Shipping to Server and Cloud with Docker
Shipping to Server and Cloud with DockerShipping to Server and Cloud with Docker
Shipping to Server and Cloud with Docker
 
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fastHow Bitbucket Pipelines Loads Connect UI Assets Super-fast
How Bitbucket Pipelines Loads Connect UI Assets Super-fast
 
Ten Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-onsTen Battle-Tested Tips for Atlassian Connect Add-ons
Ten Battle-Tested Tips for Atlassian Connect Add-ons
 
Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!Build a JIRA Server Add-on as a Microservice - You Can Do It!
Build a JIRA Server Add-on as a Microservice - You Can Do It!
 
Tempo’s Journey Into the Cloud
Tempo’s Journey Into the CloudTempo’s Journey Into the Cloud
Tempo’s Journey Into the Cloud
 
Bringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back AgainBringing Server Add-ons to the Cloud and Back Again
Bringing Server Add-ons to the Cloud and Back Again
 
Building Search for Bitbucket Cloud
Building Search for Bitbucket CloudBuilding Search for Bitbucket Cloud
Building Search for Bitbucket Cloud
 
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-OnsAtlassian Connect on Serverless Platforms: Low Cost Add-Ons
Atlassian Connect on Serverless Platforms: Low Cost Add-Ons
 
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’tsDesigning Add-ons for Atlassian Products, the Do’s & Don’ts
Designing Add-ons for Atlassian Products, the Do’s & Don’ts
 
5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems5 Essential Techniques for Building Fault-tolerant Systems
5 Essential Techniques for Building Fault-tolerant Systems
 
Know Thy Product: Tips from a Tester
Know Thy Product: Tips from a TesterKnow Thy Product: Tips from a Tester
Know Thy Product: Tips from a Tester
 
How to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest FeatureHow to Make Customer Support Your Product's Greatest Feature
How to Make Customer Support Your Product's Greatest Feature
 
How to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets SmarterHow to Write a Chatbot that Gets Smarter
How to Write a Chatbot that Gets Smarter
 
What's New with Confluence Connect
What's New with Confluence ConnectWhat's New with Confluence Connect
What's New with Confluence Connect
 
12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on12 Ways to Supercharge Your Connect Add-on
12 Ways to Supercharge Your Connect Add-on
 
Launch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service DeskLaunch into New Markets with JIRA Service Desk
Launch into New Markets with JIRA Service Desk
 
Server Add-ons for Front-end Developers
Server Add-ons for Front-end DevelopersServer Add-ons for Front-end Developers
Server Add-ons for Front-end Developers
 
Connect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions LaterConnect First, Ask Confluence Questions Later
Connect First, Ask Confluence Questions Later
 
Building for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in MindBuilding for the Future: Design Your Add-on with Tomorrow in Mind
Building for the Future: Design Your Add-on with Tomorrow in Mind
 

Ähnlich wie React for Re-use: Creating UI Components with Confluence Connect

Ähnlich wie React for Re-use: Creating UI Components with Confluence Connect (20)

React lecture
React lectureReact lecture
React lecture
 
Let's react - Meetup
Let's react - MeetupLet's react - Meetup
Let's react - Meetup
 
Server side rendering with React and Symfony
Server side rendering with React and SymfonyServer side rendering with React and Symfony
Server side rendering with React and Symfony
 
A full introductory guide to React
A full introductory guide to ReactA full introductory guide to React
A full introductory guide to React
 
React - Start learning today
React - Start learning today React - Start learning today
React - Start learning today
 
Introduction to React JS for beginners
Introduction to React JS for beginners Introduction to React JS for beginners
Introduction to React JS for beginners
 
React & The Art of Managing Complexity
React &  The Art of Managing ComplexityReact &  The Art of Managing Complexity
React & The Art of Managing Complexity
 
Introduction to React for jQuery Developers
Introduction to React for jQuery DevelopersIntroduction to React for jQuery Developers
Introduction to React for jQuery Developers
 
JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"JSLab. Алексей Волков. "React на практике"
JSLab. Алексей Волков. "React на практике"
 
Universal JS Applications with React
Universal JS Applications with ReactUniversal JS Applications with React
Universal JS Applications with React
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Intro react js
Intro react jsIntro react js
Intro react js
 
React Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptxReact Hooks Best Practices in 2022.pptx
React Hooks Best Practices in 2022.pptx
 
Lec7Handout.ppt
Lec7Handout.pptLec7Handout.ppt
Lec7Handout.ppt
 
Enhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order componentEnhance react app with patterns - part 1: higher order component
Enhance react app with patterns - part 1: higher order component
 
React js
React jsReact js
React js
 
OttawaJS - React
OttawaJS - ReactOttawaJS - React
OttawaJS - React
 
React & Redux for noobs
React & Redux for noobsReact & Redux for noobs
React & Redux for noobs
 
Fundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdfFundamental Concepts of React JS for Beginners.pdf
Fundamental Concepts of React JS for Beginners.pdf
 
react-hooks.pdf
react-hooks.pdfreact-hooks.pdf
react-hooks.pdf
 

Mehr von Atlassian

Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch Plugin
Atlassian
 

Mehr von Atlassian (20)

International Women's Day 2020
International Women's Day 2020International Women's Day 2020
International Women's Day 2020
 
10 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 202010 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 2020
 
Forge App Showcase
Forge App ShowcaseForge App Showcase
Forge App Showcase
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UI
 
Meet the Forge Runtime
Meet the Forge RuntimeMeet the Forge Runtime
Meet the Forge Runtime
 
Forge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceForge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User Experience
 
Take Action with Forge Triggers
Take Action with Forge TriggersTake Action with Forge Triggers
Take Action with Forge Triggers
 
Observability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeObservability and Troubleshooting in Forge
Observability and Troubleshooting in Forge
 
Trusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelTrusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy Model
 
Designing Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemDesigning Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI System
 
Forge: Under the Hood
Forge: Under the HoodForge: Under the Hood
Forge: Under the Hood
 
Access to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAccess to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIs
 
Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch Plugin
 
Tear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingTear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the Building
 
Nailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterNailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that Matter
 
Building Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindBuilding Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in Mind
 
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
 
Beyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsBeyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced Teams
 
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamThe Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
 
Building Apps With Enterprise in Mind
Building Apps With Enterprise in MindBuilding Apps With Enterprise in Mind
Building Apps With Enterprise in Mind
 

Kürzlich hochgeladen

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
VictorSzoltysek
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
VishalKumarJha10
 

Kürzlich hochgeladen (20)

Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Diamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with PrecisionDiamond Application Development Crafting Solutions with Precision
Diamond Application Development Crafting Solutions with Precision
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
How To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.jsHow To Use Server-Side Rendering with Nuxt.js
How To Use Server-Side Rendering with Nuxt.js
 
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
Direct Style Effect Systems -The Print[A] Example- A Comprehension AidDirect Style Effect Systems -The Print[A] Example- A Comprehension Aid
Direct Style Effect Systems - The Print[A] Example - A Comprehension Aid
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
Tech Tuesday-Harness the Power of Effective Resource Planning with OnePlan’s ...
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
The Guide to Integrating Generative AI into Unified Continuous Testing Platfo...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM TechniquesAI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
AI Mastery 201: Elevating Your Workflow with Advanced LLM Techniques
 
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
Reassessing the Bedrock of Clinical Function Models: An Examination of Large ...
 
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdfAzure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
Azure_Native_Qumulo_High_Performance_Compute_Benchmarks.pdf
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdfintroduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
introduction-to-automotive Andoid os-csimmonds-ndctechtown-2021.pdf
 

React for Re-use: Creating UI Components with Confluence Connect

  • 1. React for Reuse Creating reusable UI Components with React MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN
  • 7. Client Side We use extended JavaScript on the client side to create the UI Components. Server Side In this example we use server side JavaScript by using NodeJs. JavaScript Everywhere
  • 23. Getting Started ACE Project Create the project Use atlas-connect to create a project using the confluence template.
  • 27. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Functional Classes State
  • 28. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Functional Classes State
  • 29. Components function Clock(props) {
 return <span>{moment().format()}</span>;
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Use a function to define a Component Functional Classes State
  • 30. f(x) Functional Components Simple Defining a component as a function is very simple. Stateless Components defined as functions have no local state.
  • 32. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State
  • 33. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State
  • 34. class Clock extends React.Component {
 render() {
 return <span>{moment().format()}</span>;
 }
 } ReactDOM.render(
 <Clock />,
 document.getElementById('root')
 ); Components Functional Classes State Use a class to define a Component
  • 35. Components as Classes Classes Classes can benefit from inheritance, composition and other object orientation strategies. Stateful Components defined as classes can define their own state and lifecycle.
  • 37. Components class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } Functional Classes State
  • 38. Components class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } Functional Classes State
  • 39. Components Functional Classes class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = { date: moment() };
 } 
 render() {
 return ( <span> {this.state.date.format()} </span> );
 }
 } State Class components can have state.
  • 40. State Management Component State React provides simple state management for each component. Centralised State Redux is a centralised state management system, often simplifying your components dramatically.
  • 41. Components have a Lifecycle. Components Lifecycle Props
  • 42. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 43. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 44. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props
  • 45. Components class Clock extends React.Component { // ... componentDidMount() {
 // called after the component is added to the DOM
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 // called before the component is removed from the DOM
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({ date: moment() }); } 
 // ...
 } Lifecycle Props The lifecycle methods update the state.
  • 47. Components // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Lifecycle Props
  • 48. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 49. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 50. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props
  • 51. // src/components/Clock.js class Clock extends React.Component { render() {
 return ( <span>{this.state.date.format(this.props.dateFormat)}</span> );
 }
 } Clock.defaultProps = {
 "dateFormat": 'MMMM Do YYYY, h:mm:ss'
 }; // clock.js ReactDOM.render(
 <Clock />,
 document.getElementById('clock')
 ); ReactDOM.render(
 <Clock dateFormat='h:mm:ss'/>,
 document.getElementById('clock')
 ); Components Lifecycle Props Props are set when created and are immutable.
  • 52. Component Types Container Fetches the application data and composes the User Interface out of Presentation components. Presentation Takes the data from a Container component and presents it to the user.
  • 54. Project Components Components of Confluence Issues Add-on
  • 55.
  • 56. IssuesAppContainer Decides what to display in the page body.
  • 57. IssuesList Displays the column headings and an IssueListItem for each row.
  • 58. IssuesListItem Represents a row in the list of issues.
  • 62. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 63. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 64. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 65. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues
  • 66. loadIssues(spaceKey) {
 return (callback) => {
 AP.require('request', function (request) {
 request({
 url: '/rest/api/content',
 data: { … },
 success: (data) => {
 let issues = JSON.parse(data) .results .map(Issues.contentToIssue); callback(localIssues);
 }
 });
 });
 }
 } Load Issues loadIssues Reusable function to load issues.
  • 67. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 68. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 69. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro
  • 70. export default class IssueAppContainer extends React.Component { 
 componentDidMount() {
 this.props.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 render() {
 let createIssueClick = …;
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueAppEmpty createIssueClick={createIssueClick}/>)
 } else {
 return (<IssueAppPopulated createIssueClick={createIssueClick}
 issues={this.state.issues}/>)
 }
 }
 }
 } Issue App Components Issue App Issue List Issue Macro Use the state to decide what to render.
  • 72. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 73. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 74. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 }
  • 75. Issue App Components Issue App Issue List Issue Macro 
 export default class IssueList extends React.Component {
 render() {
 let issues = this.props.issues;
 let fields = …;
 
 return (
 <table className="list-of-issues">
 <IssueListHeadings fields={fields}/>
 <tbody>
 {issues.map((issue) => {
 return (
 <IssueLineItem key={issue.key} fields={fields} issue={issue}/>)
 })}
 </tbody>
 </table>
 );
 }
 } Presentation components have no state.
  • 77. Macro This macro shows a list of issues. Issue App Issue List Issue Macro Issue App Components
  • 79. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 80. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 81. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 }
  • 82. Issue App Components Issue App Issue List Issue Macro export default class IssueMacroContainer extends React.Component {
 
 componentDidMount() {
 this.loadIssues((issues) => {
 this.setState({issues: issues, loading: false})
 });
 }
 
 render() {
 if (this.state.loading) {
 return (<Loading/>);
 } else {
 let emptyState = (this.state.issues.length === 0);
 if (emptyState) {
 return (<IssueMacroEmpty/>)
 } else {
 return (<IssueMacroPopulated issues={this.state.issues}/>)
 }
 }
 }
 } Same concept as IssueAppContainer.
  • 86. Entry Point Import Context Render import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues"));
  • 87. Entry Point Import Context Render import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues"));
  • 88. import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues")); Entry Point Import Context Render
  • 89. import IssueMacroContainer from '../containers/IssueMacroContainer';
 
 let spaceKey = queryString.parse(location.search)["spaceKey"];
 
 ReactDOM.render(<IssueMacroContainer
 loadIssues={Issues.loadIssues(spaceKey)}/>, document.getElementById("list-issues")); Entry Point Import Context Render An entry point will render the top component.
  • 90. webpack takes modules with dependencies and generates static assets representing those modules. http://webpack.github.io/
  • 92. Webpack Configuration Config Entry Output module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 };
  • 93. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output
  • 94. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output
  • 95. module.exports = {
 resolve: {root: [__dirname + path.sep + 'assets']},
 devtool: 'source-map',
 entry:{
 app: './src/entry-points/App.js',
 dialog: './src/entry-points/NewIssueDialog.js',
 macro: './src/entry-points/Macro.js',
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ … ]
 }
 }; Webpack Configuration Config Entry Output Webpack will bundle your components into static files that a browser will understand.
  • 97. {{!< layout}}
 <div id="list-issues"/>
 <script src="/js/macro.js"></script> Recap Component State Entry HTML
  • 98. Building Using NPM to build your App
  • 99. Building Package Bundle Start {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 }
  • 100. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start
  • 101. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start
  • 102. {
 "name": "confluence-issues-addon",
 "version": "0.0.1",
 "private": true,
 "scripts": {
 "start": "node app.js",
 "bundle": "webpack",
 "watch": "webpack --watch",
 "prestart": "webpack"
 },
 "dependencies": { … },
 "devDependencies": {
 "babel-core": "^6.9.0",
 "babel-loader": "^6.2.4",
 "babel-preset-es2015": "^6.9.0",
 "babel-preset-react": "^6.5.0",
 "webpack": "^1.13.0"
 }
 } Building Package Bundle Start Babel is used to transpile the extended JavaScript.
  • 104. ES2015 Extensions, modules, dependencies, etc Standalone files, compatible with browsers, etc Transpiling
  • 105. Run a the webpack script Use npm run bundle to run the webpack script we defined in the package.json file. Building Package Bundle Start
  • 106. Building Package Bundle Start Run a the webpack script Use npm run bundle to run the webpack script we defined in the package.json file.
  • 107. Create the static JavaScript files These files are self contained and browser compatible JavaScript resources. Building Package Bundle Start
  • 108. Client app.js Refers to the application entry point component Server app.js Default name for the Node.js server entry point. Two Apps
  • 109. Create the project Use npm start to start the node app, automatically transpiling the client components. Building Package Bundle Start
  • 110. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 111. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 112. Building Package Bundle Start Create the project Use npm start to start the node app, automatically transpiling the client components.
  • 113.
  • 114. Node JS Uses NPM to package both server and client side modules. Manually Package You can still use NPM to bundle your client side components Dev Loop Restart your add-on service and call webpack on each change.
  • 115. Recap Quick recap of what we have learnt so far
  • 116. Recap Component State Entry class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Building
  • 117. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 118. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 119. class Clock extends React.Component {
 constructor(props) {
 super(props);
 this.state = {date: moment()};
 }
 
 componentDidMount() {
 this.timerID = setInterval(() => this.tick(), 1000);
 }
 
 componentWillUnmount() {
 clearInterval(this.timerID);
 }
 
 tick() {
 this.setState({date: moment()});
 }
 
 render() {
 return (<span>{this.state.date.format(this.props.dateFormat)}</span>);
 }
 }
 
 Clock.defaultProps = {
 'dateFormat': 'MMMM Do YYYY, h:mm:ss'
 };
 
 export default Clock; Recap Component State Entry Building
  • 120. ReactDOM.render(
 <Clock dateFormat='MMMM Do YYYY, h:mm:ss'/>,
 document.getElementById('clock')
 ); Recap Component State Entry Building
  • 121. module.exports = {
 resolve: { root: [__dirname + path.sep + 'assets'] },
 devtool: 'source-map',
 entry:{
 router: './assets/js/Clock'
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ …]
 }
 }; Recap Component State Entry Building
  • 122. module.exports = {
 resolve: { root: [__dirname + path.sep + 'assets'] },
 devtool: 'source-map',
 entry:{
 router: './assets/js/Clock'
 },
 output: {
 path: "./public/js",
 filename: '[name].js'
 },
 plugins: [ … ],
 module: {
 loaders: [ …]
 }
 }; Recap Component State Entry Building
  • 123. {{!< layout}}
 <div id="clock"/>
 <script src="/js/clock.js"></script> Recap Component State Entry Building
  • 124. Recap Component State Entry Building NPM Use npm to manage webpack dependencies and development lifecycle.
  • 125. Onward Related Concepts and Advanced Topics
  • 126. Onward AtlasKit Other Extensions React Components AtlasKit components are React Components. NPM Modules Define a dependency on the AtlasKit component and import it in to your own! AtlasKit
  • 131. Thank you! MATTHEW JENSEN | TEAM LEAD | ATLASSIAN | @MATTHEWJENSEN