Slide 1

Slide 1 text

1 React plus X Mars Jullian Senior UI Engineer Best practices for reusable UI components.

Slide 2

Slide 2 text

2 Why build a UI component library? • Breaking out of the monolith —> new repos for teams / products —> different frameworks & build systems. Brand & UX consistency —> Share & maintain the design & functionality across codebases easily.

Slide 3

Slide 3 text

3 Why build a UI component library? • Breaking out of the monolith —> new repos for teams / products —> different frameworks & build systems. • Brand & UX consistency —> Share & maintain the design & functionality across codebases easily.

Slide 4

Slide 4 text

4 Hi, I’m Mars. Senior Software Engineer @ I like reusable UI components.

Slide 5

Slide 5 text

5 React & Reusable UI components • JSX / no templates —> less files per component • Markup a function of state & props —> UI components can be independent of data source but still have full control over interactions. • Easily ported into different frameworks

Slide 6

Slide 6 text

6 React & Reusable UI components • JSX / no templates —> less files per component • Markup a function of state & props —> UI components can be independent of data source but still have full control over interactions. • Easily ported into different frameworks

Slide 7

Slide 7 text

7 React & Reusable UI components • JSX / no templates —> less files per component • Markup a function of state & props —> UI components can be independent of data source but still have full control over interactions. • Easily ported into different frameworks

Slide 8

Slide 8 text

8 Reusable UI components should be… …self-sufficient …easy to integrate with

Slide 9

Slide 9 text

9 Reusable UI components should be… …self-sufficient …easy to integrate with

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

11

Slide 12

Slide 12 text

12 Reusable UI components should be… …self-sufficient …easy to integrate with

Slide 13

Slide 13 text

13 Self-sufficiency: Don’t worry about data. const Bob = React.createClass({ propTypes: { tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), // ... more props } }); data / content not part of component state, should come from parent application

Slide 14

Slide 14 text

Self-sufficiency: Enough state to be useful. handleTabClick(selectedTab) { this.setState({ selectedTab }); this.props.tabEvents.onClick(selectedTab); }, renderTabContent(isOverview) { const currentTab = this.state.selectedTab; const tab = _.find(this.props.tabs, { key: currentTab }); const tabContent = tab.content; return _.isFunction(tabContent) ? tabContent() : tabContent; }, keep track of currently selected tab 14

Slide 15

Slide 15 text

15 Self-sufficiency: Enough state to be useful. handleTabClick(selectedTab) { this.setState({ selectedTab }); this.props.tabEvents.onClick(selectedTab); }, renderTabContent(isOverview) { const currentTab = this.state.selectedTab; const tab = _.find(this.props.tabs, { key: currentTab }); const tabContent = tab.content; return _.isFunction(tabContent) ? tabContent() : tabContent; }, knowing which tab is currently selected, means you know what application data / content to render

Slide 16

Slide 16 text

16 Self-sufficiency: Enough state to be useful. handleTabClick(selectedTab) { this.setState({ selectedTab }); this.props.tabEvents.onClick(selectedTab); }, renderTabContent(isOverview) { const currentTab = this.state.selectedTab; const tab = _.find(this.props.tabs, { key: currentTab }); const tabContent = tab.content; return _.isFunction(tabContent) ? tabContent() : tabContent; }, let parent application know that UI state has changed

Slide 17

Slide 17 text

17 Self-sufficiency: State initialization. const Bob = React.createClass({ propTypes: { selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }, componentWillReceiveProps() { // does nothing } }); parent application can initialize component in a certain state

Slide 18

Slide 18 text

18 const Bob = React.createClass({ propTypes: { selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }, componentWillReceiveProps() { // does nothing } }); …but selected tab cannot be overridden in subsequent updates Self-sufficiency: State initialization.

Slide 19

Slide 19 text

19 Reusable UI components should be… …self-sufficient …easy to integrate with

Slide 20

Slide 20 text

20 const Bob = React.createClass({ propTypes: { cover: React.PropTypes.element.isRequired, title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, description: React.PropTypes.string.isRequired, tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), tabEvents: React.PropTypes.shape({ onClick: React.PropTypes.func }), selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }); Ease of integration: Self-documentation.

Slide 21

Slide 21 text

21

Slide 22

Slide 22 text

22 const Bob = React.createClass({ propTypes: { cover: React.PropTypes.element.isRequired, title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, description: React.PropTypes.string.isRequired, tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), tabEvents: React.PropTypes.shape({ onClick: React.PropTypes.func }), selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }); Ease of integration: Self-documentation. specify what types of props component expects

Slide 23

Slide 23 text

23 const Bob = React.createClass({ propTypes: { cover: React.PropTypes.element.isRequired, title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, description: React.PropTypes.string.isRequired, tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), tabEvents: React.PropTypes.shape({ onClick: React.PropTypes.func }), selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }); Ease of integration: Self-documentation. specify which props are required for component to behave as expected

Slide 24

Slide 24 text

24 const Bob = React.createClass({ propTypes: { cover: React.PropTypes.element.isRequired, title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, description: React.PropTypes.string.isRequired, tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), tabEvents: React.PropTypes.shape({ onClick: React.PropTypes.func }), selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }); Ease of integration: Self-documentation. specify that component expects tabs, and exactly what the tabs should look like

Slide 25

Slide 25 text

25 const Bob = React.createClass({ propTypes: { cover: React.PropTypes.element.isRequired, title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, description: React.PropTypes.string.isRequired, tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ key: React.PropTypes.string.isRequired, displayName: React.PropTypes.string.isRequired, content: React.PropTypes.oneOfType({ React.PropTypes.func, React.PropTypes.node }).isRequired }) ), tabEvents: React.PropTypes.shape({ onClick: React.PropTypes.func }), selectedTab: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.oneOf([ TAB_OVERVIEW ]) ]) } }); Ease of integration: Self-documentation.

Slide 26

Slide 26 text

26 Ease of integration: Self-documentation. npm install gulp-react-docs

Slide 27

Slide 27 text

27 title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, // titleLogoSrc: React.PropTypes.string.isRequired, // titleString: React.PropTypes.string.isRequired Ease of integration: Fewer props.

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

29 title: React.PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.element ]).isRequired, // titleLogoSrc: React.PropTypes.string.isRequired, // titleString: React.PropTypes.string.isRequired Ease of integration: Fewer props. one flexible prop vs. two differently named props

Slide 30

Slide 30 text

30 const TabNav = React.createClass({ propTypes: { tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ // ...tab shape }) ), // hideTabs: React.PropTypes.bool }, render() { if (!this.props.tabs || !this.props.tabs.length) { return null; } // ... } }); Ease of integration: Fewer props.

Slide 31

Slide 31 text

31

Slide 32

Slide 32 text

32 const TabNav = React.createClass({ propTypes: { tabs: React.PropTypes.arrayOf( React.PropTypes.shape({ // ...tab shape }) ), // hideTabs: React.PropTypes.bool }, render() { if (!this.props.tabs || !this.props.tabs.length) { return null; } // ... } }); Ease of integration: Fewer props. decouple props by intelligently rendering tabs, instead of adding a hideTabs prop

Slide 33

Slide 33 text

33 handleTabClick(selectedTab) { this.setState({ selectedTab }); this.props.tabEvents.onClick(selectedTab); }, renderTab(tab) { return (
this.handleTabClick(tab.name) } > { tab.name }
); }, Ease of integration: Remove backdoors. good

Slide 34

Slide 34 text

34 renderTab(tab) { return (
{ tab.name }
); }, Ease of integration: Remove backdoors. backdoor into React div elements. BAD!

Slide 35

Slide 35 text

35 Ease of integration: Fill container element. margin border padding position - - - - - - - - - - - - just this your component

Slide 36

Slide 36 text

36 Ease of integration: Publish assets intelligently. …support as many build systems and developments environments as possible…

Slide 37

Slide 37 text

37 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md publish components to a package manager… …and a CDN

Slide 38

Slide 38 text

38 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md make sure components are versioned

Slide 39

Slide 39 text

39 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md transpile ES6 & JSX into ES5 & JS

Slide 40

Slide 40 text

40 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md pre-process stylesheets to replace relative asset URL’s with absolute URL’s (e.g. fonts and images)

Slide 41

Slide 41 text

41 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md compile .less into .css

Slide 42

Slide 42 text

42 Ease of integration: Publish assets intelligently. Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md Bob dist components Bob.js TabNav.js styles _tabNav.less bob.less bob.css dist.js src components Bob.jsx TabNav.jsx styles _tabNav.less bob.less dist.js CHANGELOG.md gulpfile.js package.json README.md compile JS into one file

Slide 43

Slide 43 text

43 Ease of integration: Publish assets intelligently. // Bob/src/dist.js gets compiled (without dependencies) // into Bob/dist/dist.js const Bob = require('./components/Bob.jsx'); window.YOUR_NAMESPACE = window.YOUR_NAMESPACE || {}; window.YOUR_NAMESPACE.Bob = Bob; supports teams loading code via CDN

Slide 44

Slide 44 text

44 Ease of integration: Publish assets intelligently. • Publish assets to multiple locations • Version components • Transpile JavaScript • Pre-process styles • Compile styles and JavaScript

Slide 45

Slide 45 text

45 Reusable UI components should be… …self-sufficient …easy to integrate with

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

47 Thanks, I’m @marsjosephine. Senior Software Engineer @ I like reusable UI components.