Eliminating Noice from your Code

Eliminating Noice from your Code

7a0e72a6f55811246bb5d9a946fd2e49?s=128

Radoslav Stankov

November 19, 2019
Tweet

Transcript

  1. Radoslav Stankov 23/11/2019 Eliminating noise from your code

  2. None
  3. Radoslav Stankov @rstankov blog.rstankov.com github.com/rstankov
 twitter.com/rstankov

  4. None
  5. None
  6. https://speakerdeck.com/rstankov

  7. None
  8. None
  9. Huey

  10. Huey Dewey

  11. Huey Dewey Louie

  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. None
  21. None
  22. None
  23. None
  24. None
  25. Why code gets bad overtime? !

  26. What is a good code? "

  27. None
  28. None
  29. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  30. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 } else {
 }
 });
 });
  31. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 } else {
 }
 });
 });
  32. #

  33. Is this good code? $

  34. Why not? %

  35. None
  36. ... too much information density

  37. ... too much information noise &

  38. “In cognitive psychology, cognitive load refers to the used amount

    of working memory resources. ” - WikipediA Cognitive load
  39. “Ego depletion refers to the idea that self- control or

    willpower draws upon a limited pool of mental resources that can be used up.” - WikipediA Ego depletion
  40. None
  41. None
  42. None
  43. None
  44. None
  45. None
  46. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 } else {
 }
 });
 });
  47. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 } else {
 }
 });
 });
  48. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
 }); 

  49. $('.accordion-menu .item a').each(function() {
 $(this).bind('click', function() {
 var title =

    this.className;
 var theul = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
 }); 

  50. $('.accordion-menu .item a').click(function() {
 var title = this.className;
 var theul

    = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  51. $('.accordion-menu .item a').click(function() {
 var title = this.className;
 var theul

    = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  52. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  53. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  54. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  55. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  56. <ul class="accordion-menu">
 <li class="item">
 <a href="#" class="section-1">Section 1</a>
 <ul id="section-1">


    <li>...</li>
 <li>...</li>
 <li>...</li>
 </ul>
 </li>
 </ul>
  57. $('.accordion-menu .item a').click(function() {
 var title = this.className;
 var theul

    = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  58. $('.accordion-menu .item a').click(function() {
 var title = this.className;
 var theul

    = $('#' + title);
 if (theul.length > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  59. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul');
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  60. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul');
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  61. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul');
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 } else {
 theul.slideUp();
 var theli = dropa.parent('li');
 theli.animate({'padding-bottom': '20px'});
 }
 }
 });
  62. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul');
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 var theli = dropa.parent('li')
 theli.animate({'padding-bottom': '20px'});
 }
 });
  63. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul');
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 var theli = dropa.parent('li')
 theli.animate({'padding-bottom': '20px'});
 }
 });
  64. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 }
 });
  65. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 if (theul.css('display') == 'none') {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 }
 });
  66. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 if (theul.is(':visible')) {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 }
 });
  67. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 if (theul.is(':visible')) {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 }
 });
  68. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 if (theul.is(':visible')) {
 theul.slideDown()
 } else {
 theul.slideUp()
 }
 }
 });
  69. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 theul.slideToggle();
 }
 });
  70. $('.accordion-menu .item a').click(function() {
 var theul = $(this).next('ul')
 if (theul.length

    > 0) {
 theul.slideToggle();
 }
 });
  71. $('.accordion-menu .item a').click(function() {
 $(this).next('ul').slideToggle();
 });


  72. $('.accordion-menu .item a').click(function() {
 $(this).next('ul').slideToggle();
 });


  73. $('.accordion-menu .item a').click(function() {
 $(this).next('ul').slideToggle();
 });


  74. $('.accordion-menu .item a').live('click', function() {
 $(this).next('ul').slideToggle();
 });

  75. $('.accordion-menu .item a').live('click', function() {
 $(this).next('ul').slideToggle();
 });

  76. $('.accordion-menu .item a').live('click', function() {
 $(this).next('ul').slideToggle();
 });

  77. $('.accordion-menu .item a').click(function() {
 $(this).next('ul').slideToggle();
 });


  78. $(document).on('click', '.accordion-menu .item a', function() {
 $(this).next('ul').slideToggle();
 });


  79. $(document).on('click', '.js-accordion-item a', function() {
 $(this).next('js-accordion-section').slideToggle();
 });

  80. $(document).on('click', '[data-accordion="item"]', function() {
 $(this).next('[data-accordion="section"]').slideToggle();
 });

  81. None
  82. None
  83. None
  84. None
  85. “It is better to be a good programmer with great

    habits, than a great programmer.” - Kent Beck
  86. Broken windows theory

  87. “Social psychologists and police officers tend to agree that if

    a window in a building is broken and is left unrepaired, all the rest of the windows will soon be broken.” - WikipediA Broken windows theory
  88. The Boy Scout Rule

  89. “Leave your code better than you found it.” The Boy

    Scout Rule
  90. Radoslav Stankov 05/05/2018 Eliminating noise from your code

  91. Radoslav Stankov 05/05/2018 Eliminating ' Code Smells '

  92. None
  93. Code smell is any symptom in the source code of

    a program that possibly indicates a deeper problem. Code smells
  94. Code smells are usually not bugs—they are not technically incorrect

    and don't currently prevent the program from functioning. Instead, they indicate weaknesses in design that may be slowing down development or increasing the risk of bugs or failures in the future. Code smells
  95. Code refactoring is a disciplined technique for restructuring an existing

    body of code, altering its internal structure without changing its external behavior Refactoring
  96. None
  97. None
  98. Refactoring

  99. #1 Useless Code

  100. function LoadMoreBaseOnDevice(props) { return ( <> <Device.Mobile> <LoadMoreButton {...props} />

    </Device.Mobile> <Device.TabletOrDesktop> <LoadMoreOnScroll {...props} /> </Device.TabletOrDesktop> </> ); }
  101. function LoadMoreBaseOnDevice(props) { return ( <Device.Mobile> {isMobile => isMobile ?

    ( <LoadMoreButton {...props} /> ) : ( <LoadMoreOnScroll {...props} /> ) } </Device.Mobile> ); }
  102. function LoadMoreBaseOnDevice(props) { const isMobile = useIsMobile(); return isMobile ?

    ( <LoadMoreButton {...props} /> ) : ( <LoadMoreOnScroll {...props} /> ); }
  103. <Button onClick={(event) => click(event)} />

  104. <Button onClick={(event) => click(event)} />

  105. <Button onClick={click} />

  106. function Component({ array }) { return ( <React.Fragment> {array &&

    array.length > 0 && array.map(item => <Item item={item} />)} </React.Fragment> ); }
  107. function Component({ array }) { return ( <React.Fragment> {array &&

    array.length > 0 && array.map(item => <Item item={item} />)} </React.Fragment> ); }
  108. function Component({ array }) { return ( <React.Fragment> {array &&

    array.map(item => <Item item={item} />)} </React.Fragment> ); }
  109. function Component({ array }) { return ( <React.Fragment> {array.map(item =>

    <Item item={item} />)} </React.Fragment> ); }
  110. function Component({ array }) { return ( <React.Fragment> {array.map(item =>

    <Item item={item} />)} </React.Fragment> ); }
  111. function Component({ array }) { return array.map(item => <Item item={item}

    />); }
  112. <ABTest variant="newsletterForm"> {(variant, complete) => { return variant == 'left'

    ? ( viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.leftForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.leftForm} onSubscribe={complete} /> ) ) : viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.topForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.topForm} onSubscribe={complete} /> ); }} </ABTest>
  113. <ABTest variant="newsletterForm"> {(variant, complete) => { return variant == 'left'

    ? ( viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.leftForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.leftForm} onSubscribe={complete} /> ) ) : viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.topForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.topForm} onSubscribe={complete} /> ); }} </ABTest>
  114. <ABTest variant="newsletterForm"> {(variant, complete) => variant == 'left' ? (

    viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.leftForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.leftForm} onSubscribe={complete} /> ) ) : viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.topForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.topForm} onSubscribe={complete} /> ) } </ABTest>;
  115. #2 Too Much Conditional Logic

  116. None
  117. None
  118. <ABTest variant="newsletterForm"> {(variant, complete) => variant == 'left' ? (

    viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.leftForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.leftForm} onSubscribe={complete} /> ) ) : viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.topForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.topForm} onSubscribe={complete} /> ) } </ABTest>;
  119. <ABTest variant="newsletterForm"> {(variant, complete) => variant == 'left' ? (

    viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.leftForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.leftForm} onSubscribe={complete} /> ) ) : viewer ? ( !viewer.isSubscribedToNewsletter && ( <SubscribeForm email={viewer.email} className={style.topForm} contentClassName={styles.hideField} onSubscribe={complete} /> ) ) : ( <SubscribeForm className={styles.topForm} onSubscribe={complete} /> ) } </ABTest>;
  120. <ABTest variant="newsletterForm"> {(variant, complete) => variant == 'left' ? (

    <SubscribeForm className={styles.leftForm} onSubscribe={complete} viewer={viewer} /> ) : ( <SubscribeForm className={style.topForm} onSubscribe={complete} viewer={viewer} /> ) } </ABTest>
  121. <ABTest variant="newsletterForm"> {(variant, complete) => variant == 'left' ? (

    <SubscribeForm className={styles.leftForm} onSubscribe={complete} viewer={viewer} /> ) : ( <SubscribeForm className={style.topForm} onSubscribe={complete} viewer={viewer} /> ) } </ABTest>
  122. <ABTest variant="newsletterForm"> {(variant, complete) => ( <SubscribeForm className={variant === 'left'

    ? styles.leftForm : styles.topForm} onSubscribe={complete} viewer={viewer} /> )} </ABTest>
  123. function Card({ card }) { if (card.type === 'news') {

    return <NewsCard card={card} />; } if (card.type === 'post') { return <PostCard card={card} />; } if (card.type === 'user') { return <UserCard card={card} />; } return <DefaultCard card={card} />; }
  124. const CARDS = { news: NewsCard, post: PostCard, user: UserCard,

    }; function Card({ card }) { const Component = CARDS[card.type] || DefaultCard; return <Component card={card} />; }
  125. function ShareButton({ sharable, medium }) { const icon = medium

    === 'twitter' ? <IconTwitter /> : <IconFacebook />; const onClick = () => { if (medium === 'facebook') { shareOnFacebook(sharable); } else { shareOnTwitter(sharable); } }; return ( <Button icon={medium.icon} title={`Share on ${capitalize(medium)}`} onClick={onClick} /> ); }
  126. function ShareButton({ sharable, medium }) { const icon = medium

    === 'twitter' ? <IconTwitter /> : <IconFacebook />; const onClick = () => { if (medium === 'facebook') { shareOnFacebook(sharable); } else { shareOnTwitter(sharable); } }; return ( <Button icon={medium.icon} title={`Share on ${capitalize(medium)}`} onClick={onClick} /> ); }
  127. const MEDIUMS = { twitter: { icon: <IconTwitter className={styles.twitter} />,

    title: 'Share on Twitter', share: shareOnTwitter, }, facebook: { icon: <IconFacebook className={styles.facebook} />, title: 'Share on Facebook', share: shareOnFacebook, }, }; function ShareButton({ sharable, medium }) { const medium = MEDIUMS[medium]; return ( <Button icon={medium.icon} title={medium.title} onClick={() => medium.share(sharable)} /> ); }
  128. const MEDIUMS = { twitter: { icon: <IconTwitter className={styles.twitter} />,

    title: 'Share on Twitter', share: shareOnTwitter, }, facebook: { icon: <IconFacebook className={styles.facebook} />, title: 'Share on Facebook', share: shareOnFacebook, }, linkedin: { icon: <IconLinkedIn className={styles.linkedIn} />, title: 'Share on LinkedIn', share: shareOnLinkedIn, }, }; function ShareButton({ sharable, medium }) { const medium = MEDIUMS[medium]; return ( <Button icon={medium.icon} title={medium.title} onClick={() => medium.share(sharable)} />
  129. interface IProps { sharable: ISharable; medium: keyof typeof MEDIUMS; }

  130. http://blog.rstankov.com/replace-conditional-with-map-refactoring/

  131. #3 Too DRY code

  132. None
  133. Good DRY

  134. The bad side of DRY

  135. const API_GET = '/api/:source_type/:id'; const API_GET_DETAILS = '/api/:source_type/:id/details'; const API_GET_VERSION

    = '/api/:source_type/:id/on/:year/:date/:month'; const API_GET_SUBTYPE = '/api/:source_type/:id/sub/:subtype/:subtype_id';
  136. const API = 'api'; const SOURCE_ID = '/:id'; const SOURCE_DATE

    = '/:year/:month/:day'; const SUBTYPE = '/:subtype'; const SUBID = '/:subtype_id'; const API_GET = API + SOURCE_TYPE + SOURCE_ID; const API_GET_DETAILS = API + SOURCE_TYPE + SOURCE_ID + '/details'; const API_GET_VERSION = API + SOURCE_TYPE + SOURCE_ID + '/on' + SOURCE_DATE; const API_GET_SUBTYPE = API + SOURCE_TYPE + SOURCE_ID + '/sub' + SUBTYPE + SUBID;
  137. function path(rest = '') { return `${BASE_PATH}${rest}`; } function token(string)

    { return `${SEPARATOR}${string}`; } const SEPARATOR = '/'; const API = 'api'; const SOURCE_TYPE = ':source_type'; const SOURCE_ID = ':id'; const YEAR = ':year'; const MONTH = ':month'; const DAY = ':day'; const SUBTYPE = ':subtype'; const SUBID = ':subtype_id'; const DETAILS = 'details'; const ON = 'on'; const SUB = 'sub'; const BASE_PATH = token(API) + token(SOURCE_TYPE) + token(SOURCE_ID); const DATE_PART = token(YEAR) + token(MONTH) + token(DAY); const SUB_PART = token(SUBTYPE) + token(SUBID); const API_GET = path(); const API_GET_DETAILS = path(token(DETAILS)); const API_GET_VERSION = path(token(ON) + token(DATE_PART)); const API_GET_SUBTYPE = path(token(SUB) + token(SUB_PART));
  138. None
  139. Don't repeat yourself (DRY) is a principle of software development

    aimed at reducing repetition of software patterns, replacing it with abstractions or using data normalization to avoid redundancy. Don't repeat yourself
  140. The DRY principle is stated as "Every piece of knowledge

    must have a single, unambiguous, authoritative representation within a system" Don't repeat yourself
  141. #4 Single Level of Abstraction

  142. SLAP Single Level of Abstraction Principle SLAP

  143. <Dropdown onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />

  144. <Drobdown trigger="click" /> <Dropdown trigger="hover" />

  145. function Cart() { const [cart, setData] = useState<T>(selectData(DataSource)); const callback

    = useCallback(selectData, []); useEffect(() => { const removeChangeListener = DataSource.addChangeListener(() => setData(callback(DataSource)), ); return removeChangeListener; }, [callback]); return ( <aside> <h2> Cart <small>({cart.length})</small> </h2> <ul> {cart.map(item => ( <li key={item.product.id}> <CartItem item={item} /> </li> ))} </ul> </aside> ); }
  146. function Cart() { const [cart, setData] = useState<T>(selectData(DataSource)); const callback

    = useCallback(selectData, []); useEffect(() => { const removeChangeListener = DataSource.addChangeListener(() => setData(callback(DataSource)), ); return removeChangeListener; }, [callback]); return ( <aside> <h2> Cart <small>({cart.length})</small> </h2> <ul> {cart.map(item => ( <li key={item.product.id}> <CartItem item={item} /> </li> ))} </ul> </aside> ); }
  147. function Cart() { const [cart, setData] = useState<T>(selectData(DataSource)); const callback

    = useCallback(selectData, []); useEffect(() => { const removeChangeListener = DataSource.addChangeListener(() => setData(callback(DataSource)), ); return removeChangeListener; }, [callback]); return ( <aside> <h2> Cart <small>({cart.length})</small> </h2> <ul> {cart.map(item => ( <li key={item.product.id}> <CartItem item={item} /> </li> ))} </ul> </aside> ); }
  148. function Cart() { const cart = useData(getCartData); return ( <aside>

    <h2> Cart <small>({cart.length})</small> </h2> <ul> {cart.map(item => ( <li key={item.product.id}> <CartItem item={item} /> </li> ))} </ul> </aside> ); }
  149. function useData<T>(selectData: (data: DataSourceClass) => T): T { const [data,

    setData] = useState<T>(selectData(DataSource)); const callback = useCallback(selectData, []); useEffect(() => { const removeChangeListener = DataSource.addChangeListener(() => setData(callback(DataSource)), ); return removeChangeListener; }, [callback]); return data; }
  150. None
  151. ( Duplicated Code ) Long Method * Large Class +

    Long Parameter List , Shotgun Surgery - Feature Envy . Data Clumps / Case Statements 0 Parallel Inheritance Hierarchies 1 Speculative Generality 2 Temporary Field 3 Message Chains 4 Middle Man 5 Alternative Classes with Different Interfaces 6 Refused Bequest 7 Comments 8 Repetitive Boilerplate 9 God object ' : and more ...
  152. None
  153. None
  154. None
  155. None
  156. Thanks ;

  157. https://speakerdeck.com/rstankov

  158. None