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 Hi, I’m Mars. Senior UI Engineer @ I like reusable UI components (a lot).

Slide 3

Slide 3 text

3 The challenge. •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 The solution? Reusable UI components! …built using React!

Slide 5

Slide 5 text

5 React: No templates needed. const React = require(‘react'); const HelloWorld = React.createClass({ render() { const mood = 'good'; const greeting = mood === 'good' ? 'Hello' : 'Leave me alone'; return (
{ `${greeting}, world!` }
); } }); module.exports = HelloWorld; UI Logic Markup

Slide 6

Slide 6 text

6 React: Component-ize. const React = require('react'); const Hello = require('myComponents/Hello.jsx'); const World = require('myComponents/World.jsx'); const HelloWorld = React.createClass({ render() { return (
); } }); module.exports = HelloWorld; Components

Slide 7

Slide 7 text

7 React: Declarative framework. const React = require('react'); const HelloWorld = React.createClass({ render() { const greeting = this.props.mood === 'good' ? 'Hello' : 'Leave me alone'; return (
{ `${greeting}, World!` }
); } }); module.exports = HelloWorld; Props

Slide 8

Slide 8 text

8 React & Reusable UI components •JSX / no templates —> less files per component •State & Props —> UI components can be independent of data source but still have full control over interactions. •Easily ported into different frameworks

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 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 13

Slide 13 text

13 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

Slide 14

Slide 14 text

14 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 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; }, let parent application know that UI state has changed

Slide 16

Slide 16 text

16 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 17

Slide 17 text

17 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 18

Slide 18 text

18 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 19

Slide 19 text

19 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 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. specify that component expects tabs, and exactly what the tabs should look like

Slide 21

Slide 21 text

21 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 22

Slide 22 text

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

Slide 23

Slide 23 text

23 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 24

Slide 24 text

24 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 25

Slide 25 text

25 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 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

30 @marsjosephine Feedback: http://bit.ly/28Rb3aW