Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building the Media Block in ReactJS

Building the Media Block in ReactJS

Your component API is an interface that needs designing.

Nicole Sullivan

June 09, 2015
Tweet

Other Decks in Technology

Transcript

  1. BUILDING THE MEDIA BLOCK IN REACTJS the API is an

    interface that needs to be designed https://www.flickr.com/photos/darpi/212323100/in/photolist-jLdg7-4zEiwa-nwFCR-cZVtBE-4NHtnv-7daytd-pce3y-4NH3fh-5TLS72-58aMX7-58aMVN-a3826y-gVDVKr-8DRhUB-nEdkNf-6tnApQ- fqJRqv-4NMHx7-7fUPoM-cNu2W-8etirE-o7PZA-wsB4L-7ABatu-8LyEF5-7iEjwY-3faCd1-9Gn1fF-4qYRms-JUKbE-7B97bb-69Uer5-5DQMzG-f4ZgsG-7TvUJp-5zyhEh-65naZh-nBkNs-5eCcAF-sErjv-4ePHGY-6kV1Q4- nxWYX-dpGR5-55vgqd-5scyot-5t7uJJ-nLQn5y-9Njbu-79B4aw
  2. WHAT WILL WE COVER? ❖ What is a media block?

    ❖ Why use ReactJS ❖ Making a media block in ReactJS
  3. WHAT DO WE KNOW? ! ❖ Can be nested ❖

    Optional right button ❖ Must clearfix
  4. WHAT DON’T WE KNOW? ❖ Image width and decoration vary

    ❖ Right content is unknown ❖ Width unknown
  5. A FEW LINES OF HTML... <div class="media attribution"> <a href="http://twitter.com/stubbornella"

    class="img"> <img src="mini.jpg" alt="Stubbornella" /> </a> <div class="bd">@Stubbornella 14 minutes ago</div> </div>
  6. I <3 HTML, WHY USE REACT? ❖ html consistency (modal

    debugging) ❖ smaller api (fewer breaking changes) ❖ don’t need to go everywhere a component was used to update the dom if it changes ❖ lightning fast dom updates when data changes ❖ not managing those updates manually ❖ simpler APIs (tabs example)
  7. WHAT ARE THE ELEMENTS OF A REACT INTERFACE ❖ Elements

    ❖ Attributes ❖ Nested children ❖ Data Sounds a lot like html, right?
  8. HOW DO YOU MAKE A MEDIA COMPONENT? in React Code

    var Media = React.createClass({ ! });
  9. SO THEN, HOW WOULD YOU USE IT? It’s kind of

    like HTML, it’s called jsx <Media> My very first React component! </Media>
  10. BUT, WE NEED TO EXPORT the Media component, so it

    can be used in other files module.exports = { Media: Media };
  11. DESCRIBE IT WITH A RENDER FUNCTION This tells the browser

    what HTML to render. var Media = React.createClass({ render: function () { return ( <p> {this.props.children} </p> ); } }); just render a paragraph, for now tell React where to render children
  12. LET’S TRY IT! Sweet, this is gonna be rad. <Media>

    This text is our component’s children! </Media> ! ! ! ! <p> This text is our component’s children! </p>
  13. LET’S TRY IT! Sweet, this is gonna be rad. <Media>

    This text is our component’s children! </Media> ! ! ! ! <p> This text is our component’s children! </p> When React renders that component we get this html
  14. LET’S TRY IT! Sweet, this is gonna be rad. <Media>

    This text is our component’s children! </Media> ! ! ! ! <p> This text is our component’s children! </p> {this.props.children} When React renders that component we get this html
  15. NOT VERY USEFUL, RIGHT? You'd never use react to render

    a simple paragraph because it already understands <p> tags, you can just use them!
  16. CREATE A SOURCE ATTRIBUTE We’ll use it in our render

    function to set the src of the image <Media leftImageSource='http://placehold.it/50x50'> Media block content </Media> React props are a lot like HTML attributes
  17. SO, LET’S ADD AN HREF ATTRIBUTE <Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com'> Media

    block content </Media> Our render function will make this into a link wrapper (if it is set)
  18. WE NEED AN ALT ATTRIBUTE <Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile

    photo"> Media block content </Media> Our render function will make this into an alt attribute on the <img> tag
  19. WE NEED A HEIGHT AND WIDTH <Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle'

    leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px"> Media block content </Media> Explicit height and width allow the browser to do a single rendering/painting pass
  20. SET THE GUTTER BETWEEN IMAGE AND CONTENT with the spacing

    attribute <Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" leftImageSpacing="medium"> Media block content </Media> For when the 10px default spacing isn’t right, like a tiny icon with text
  21. FOR EXAMPLE, IT’S RESPONSIVE via the stacksize (breakpoint) attribute <Media

    leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" leftImageSpacing="medium" stackSize='medium'> Media block content </Media> stack size puts the image above the text at the breakpoint
  22. THIS IS GETTING OUT OF HAND! But we forgot something,

    the media block can have a right image
  23. ADD THE RIGHT IMAGE PROPERTIES This is out of control,

    we are going the wrong way! <Media leftImageSource='http://placehold.it/50x50' leftImageHref='http://www.google.com' leftImageAlignment='middle' leftImageAlt="profile photo" leftImageHeight="50px" leftImageWidth="50px" rightImageSource='http://placehold.it/50x50' rightImageHref='http://www.google.com' rightImageAlignment='middle' rightImageAlt="profile photo" rightImageHeight="50px" rightImageWidth="50px" rightImageSpacing="medium" bodyAlignment='middle' stackSize='medium'> Media block content </Media> right image props
  24. PROS/CONS ❖ It’s very explicit, we know what each thing

    does What works? ❖ We're basically recreating html in React, yuck! (we shouldn’t make a new different alt attribute! ❖ We have image properties and media properties all mixed up ❖ We have too many properties What doesn’t work?
  25. OUR IMAGES AS JSON kinda weird, but it might work…

    var images = [ { "src": "http://placehold.it/50x50", "href": "http://www.google.com", "alignment": "middle", "alt": "profile photo", "height": "50px", "width": "50px" }, { "src": "http://placehold.it/50x50", "href": "http://www.google.com", "alignment": "middle", "alt": "profile photo", "height": "50px", "width": "50px" } ];
  26. JSON IN THE MEDIA passed in as another property <Media

    images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
  27. JSON IN THE MEDIA passed in as another property <Media

    images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media> {curly braces} mean it’s a JavaScript variable rather than a string
  28. JSON IN THE MEDIA passed in as another property <Media

    images={images} bodyAlignment='middle' stackSize='medium'> Media block content </Media> json goes into the images attribute {curly braces} mean it’s a JavaScript variable rather than a string
  29. What works? What doesn’t work? PROS/CONS ❖ abstraction of passing

    in JSON means all the code isn't in the same place ❖ weird to have JSON in the middle of what looks like markup ❖ still reinventing html attributes of an <img> tag ❖ cleaner separation of concerns (media takes care of media stuff, rather than the properties of its children)
  30. PARSING CHILDREN We decided to try including the images as

    children https://www.flickr.com/photos/i-am-mclovin/16535518502/in/photolist-rcbRyA-nshV4n-eAaqTz-bCfUFZ-jH4tBF-pctQQD-qNmfZS-eT3GMZ-bTJsji-N8LkW-iCxgoA-7JDTp2-mPGu7V-dV4m7G-igpkaV-dRobZv-mnUN9i-igoYgJ-bCzBBi- f9tdxa-oMiWTE-b6LMzz-rcTY6S-dYq12b-qUh6hV-f6oFCx-pmwC9Z-hNLucH-moYnBt-6uGwja-aRrBm4-mPGGDB-igp6YC-f8b3QR-igpkXB-igoY3C-o62zzh-iC3JNn-9217QQ-D3JPG-pcHyUy-pprMfU-igoJAg-hgRxSL-pqomg9- ahQDpD-4LkbKg-hNLcDy-igoJb8-9STs34
  31. PARSING CHILDREN better, everything is normal html! But, it has

    a few drawbacks <Media> <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px" > <p>My media content</p> <img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px" > </Media>
  32. What works? What doesn’t work? PROS/CONS ❖ The images and

    body content need to be in a very particular order, it seems weird to expose that to the user ❖ Violates the "build components you can use without understanding CSS” principle ❖ Normal HTML ❖ Facebook does it this way
  33. WHAT *ELSE* DOESN’T WORK? ❖ We could loop over children

    and reorder them, but how do we tell the difference between content images and media images? ❖ We were still discovering React, and didn't know how to loop over children yet ❖ React provides handy error messages and property validations. We would lose out on that if we made the images children ❖ Facebook's images aren't optional, so it's a different case
  34. FIRST, WE MAKE OUR IMAGES var leftImage = <img src='http://placehold.it/

    50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px">; ! var rightImage = <img src='http://placehold.it/ 50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width=“50px">;
  35. NEXT, WE MAKE OUR MEDIA OBJECT this looks similar to

    the JSON example <Media leftImage={leftImage} rightImage={rightImage} bodyAlignment='middle' stackSize='medium'> Media block content </Media>
  36. NEXT, WE MAKE OUR MEDIA OBJECT this looks similar to

    the JSON example <Media leftImage={leftImage} rightImage={rightImage} bodyAlignment='middle' stackSize='medium'> Media block content </Media> left and right images are passed into attributes
  37. YOU CAN EVEN WRITE IT like this if you really

    want to <Media leftImage={<img src='http://placehold.it/50x50' href='http://www.google.com' alignment='middle' alt="profile photo" height="50px" width="50px">} bodyAlignment='middle' stackSize='medium'> Media block content </Media> image component directly in the attribute property
  38. What works? What doesn’t work? PROS/CONS ❖ HTML inside an

    attribute (in the latter example) is a bit odd, though it does have advantages. ❖ React passes default html attributes in to the resulting img tag, so we don't have to do anything special with height, width, src, aria and alt. ❖ We separate concerns and the image takes care of it's own properties ❖ No need to parse content
  39. WHAT *ELSE* DOESN’T WORK? ❖ href will be passed through.

    So our image will have an href attribute. I like clean html, and that feels weird to me! <div class="media"> <a href="styleguide.pivotal.io"> <img href="styleguide.pivotal.io" /> ... Yuck!
  40. WE CONSIDERED GOING BACK TO PROPERTIES… but decided we should

    make our own <img> wrapper <Media leftImageHref="styleguide.pivotal.io"> to property or not to property?
  41. GOAL: OUTPUTS A SIMPLE <IMG> TAG but won't pass through

    attributes that don't make sense like href
  42. STEP 2: EXPORT THE IMAGE the same way we did

    the Media component module.exports = {Image};
  43. STEP 3: GET ITS PROPERTIES and render an image var

    Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } });
  44. STEP 3: GET ITS PROPERTIES and render an image var

    Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } }); get the properties we need
  45. STEP 3: GET ITS PROPERTIES and render an image var

    Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } }); get the properties we need build the image from our properties
  46. STEP 3: GET ITS PROPERTIES and render an image var

    Image = React.createClass({ render() { var {href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src} className={classes}>; ! return href ? <a {...{href}}>{image}</a> : image; } }); get the properties we need build the image from our properties if we have a link, wrap the image in an <a> tag
  47. STEP 4: MAKE IT RESPONSIVE by handling the responsive boolean

    var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src}>; return href ? <a {...{href}}>{image}</a> : image; } });
  48. STEP 4: MAKE IT RESPONSIVE by handling the responsive boolean

    var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var image = <img {...other} src={src}>; return href ? <a {...{href}}>{image}</a> : image; } }); get the responsive property
  49. STEP 4: MAKE IT RESPONSIVE and setting the img-responsive class

    if the boolean is true var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); ! ! ! ! ! ! ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  50. STEP 4: MAKE IT RESPONSIVE and setting the img-responsive class

    if the boolean is true var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); ! ! ! ! ! ! ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } }); add this class if this evaluates to true
  51. STEP 4: MAKE IT RESPONSIVE and setting the img-responsive class

    if the boolean is true var Image = React.createClass({ render() { var {responsive, href, src, children, className, ...other} = this.props; ! var classes = classnames({'img-responsive': responsive}, className); ! ! ! ! ! ! ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } }); add this class if this evaluates to true then, put the class on the image
  52. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  53. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } }); responsive has to be true or false
  54. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  55. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } }); the href is a string
  56. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  57. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } }); src is a string and required
  58. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  59. STEP 5: VALIDATE PROPERTIES var Image = React.createClass({ propTypes: {

    responsive: types.bool, href: types.string, src: types.string.isRequired }, ! render() { var {responsive, href, src, children, className, ...other} = this.props; var classes = classnames({'img-responsive': responsive}, className); ! var image = <img {...other} src={src} className={classes}>{children}</img>; return href ? <a {...{href}}>{image}</a> : image; } });
  60. OUR “AH-HA” MOMENT Users are still needing to specify too

    many things to get this component to work, they might as well just write html!
  61. ELEMENTS FTW we can simplify our interface further https://www.flickr.com/photos/rejik/14681743931/in/photolist-onnLY4-nPYfhm-ed6PWM-bvWmjA-mMGE1V-j88ToM-ngTbpk-nUg38a-9n1hgv-4KZr2Z-nucMef-dd5exw-9eyaqy-8QWK1i-eaTuFL-4RbvFX-7kiwo3-7NqP2a-4R1KYB- mZEx1J-5iV12q-39v9f8-bqt2rx-7zvWs-9eyamJ-7JKZAh-hiwiDa-poG8fx-ehZRGj-684GeT-pPeQGL-efRP9f-icXKJY-aNxWqT-9niyKk-ouarpw-bmC5SK-7s5DNV-bqt3F8-bqsZ24-mZCLWp-86YqXk-e6ERub-bqtEL8-8K3pJf-

    kik4tg-8yYivi-8fi3Ep-dVohpu-fzmggH https://www.flickr.com/photos/rejik/14681743931/in/photolist-onnLY4-nPYfhm-ed6PWM-bvWmjA-mMGE1V-j88ToM-ngTbpk-nUg38a-9n1hgv-4KZr2Z-nucMef-dd5exw-9eyaqy-8QWK1i-eaTuFL-4RbvFX-7kiwo3-7NqP2a-4R1KYB- mZEx1J-5iV12q-39v9f8-bqt2rx-7zvWs-9eyamJ-7JKZAh-hiwiDa-poG8fx-ehZRGj-684GeT-pPeQGL-efRP9f-icXKJY-aNxWqT-9niyKk-ouarpw-bmC5SK-7s5DNV-bqt3F8-bqsZ24-mZCLWp-86YqXk-e6ERub-bqtEL8-8K3pJf- kik4tg-8yYivi-8fi3Ep-dVohpu-fzmggH
  62. DESIGNERS ONLY USE 2 KINDS OF ALIGNMENT ❖ Traditional media

    with everything top aligned ! ! ! ❖ “Flag” component a la Harry Roberts middle aligns
  63. WE MADE THESE TWO USE CASES DEAD SIMPLE ❖ We

    changed the media component to default to top alignment if nothing else was specified. ❖ We created the <flag> component <Flag leftImage={refreshImage}> refresh </Flag>
  64. What works? What doesn’t work? PROS/CONS ❖ engineers don't always

    know what the flag object is, documentation and teaching help ❖ with Flag and Media, we no longer need to specify alignment unless we want something weird
  65. var sortableCols = [ { name: 'name', title: 'Name', sortable:

    true }, { name: 'instances', title: 'Instances', sortable: true, align: 'center' }, { name: 'cpu', title: 'CPU', sortable: true, align: 'right' }, { name: 'synergy', title: 'Synergy', align: 'left' } ]
  66. TABS IN BOOTSTRAP <div role="tabpanel"> ! <!-- Nav tabs -->

    <ul class="nav nav-tabs" role="tablist"> <li role="presentation" class=“active"> <a href="#home" aria-controls="home" role="tab" data-toggle=“tab">Home</a> </li> <li role=“presentation"> <a href="#profile" aria-controls="profile" role="tab" data-toggle=“tab”> Profile</a> </li> </ul> ! <!-- Tab panes --> <div class="tab-content"> <div role="tabpanel" class="tab-pane active" id="home">...</div> <div role="tabpanel" class="tab-pane" id="profile">...</div> </div> ! </div>
  67. TABS IN REACT <SimpleTabs defaultActiveKey={1}> <Tab eventKey={1} tab="Home"> ... </Tab>

    <Tab eventKey={2} tab="Profile"> ... </Tab> </SimpleTabs>
  68. GOOD DESIGN PRINCIPLES ❖ Many drawers - Tom O ❖

    Set good defaults ❖ User shouldn’t need to understand CSS to use it ❖ Make tiny components with one job (same as CSS) ❖ Allow flexibility ❖ Prefer a complex implementation over a complex interface what has worked for us
  69. WHO ARE YOUR USERS? component creators and maintainers, contributors, developers

    building features, actual product users https://www.flickr.com/photos/fabiansociety/16300828766/in/photolist-qQs1tQ-qAa8pJ-pVJmYw-qxNcH4-qAaDuJ-qSHJsr-5SDe5H-josG7R-dxrFDm-e6S4TN-fddCLi-po7JuN-d21PZN-ax7LAK-qBLEie-dEMphp-byfU17-nPjAPc-eZ7ooX- ctHbf5-g5QFS-naVVhZ-cFgo6s-akEb2Q-qUQi3c-aGJ83i-627cGv-aRFFNx-nSyXpr-dyXFU7-aupkvk-buYgB2-nj7Xyv-jHSXR5-9eAqzK-eNqYdm-a4GaUk-qiFrdF-dy1QsG-bPqzrk-9dEUm7-n7cmgE-gJNeKz-nigszh-mi4QjT-s76Yxa-
  70. USE ALL THE TOOLS IN YOUR TOOLBOX ❖ Elements (built

    in and custom) ❖ Attributes (simple and objects) ❖ JSON ❖ Children
  71. WE OPEN SOURCED EVERYTHING Please do give us feedback npm:

    https://www.npmjs.com/search?q=pui github: github.com/pivotal-cf/pivotal-ui