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

Refactoring a Legacy Application with React

Avatar for Ali Orlando Ali Orlando
January 11, 2018

Refactoring a Legacy Application with React

Testability, UI consistency, code reuse, maintainability. These are the properties of a healthy codebase… and what most legacy applications are lacking. If you’re struggling with an application on life support, ReactJS can breathe new life into your front-end. Attendees will learn a proven approach for implementing ReactJS in a jQuery application and the problems you are likely to face along the way. This session covers why you would consider a refactor, setting goals for your desired outcome, an approach to adding ReactJS incrementally, and managing application state.

Avatar for Ali Orlando

Ali Orlando

January 11, 2018
Tweet

Other Decks in Technology

Transcript

  1. @fed_goose Legacy Application Characteristics • Low/No Test Coverage • Tests

    aren’t added with new changes • Changes are painful • Code is coupled & difficult to navigate • Application behavior is undocumented • Little confidence that changes won’t break your application
  2. @fed_goose What we’ll cover… • Why Refactor • Why OnShift

    chose React for refactoring • Example of refactoring a jQuery application with React • Pros & Cons of using Redux to manage state shared between frameworks • Example of using Redux • Additional lessons learned along the way
  3. @fed_goose -Martin Fowler, “Refactoring: Improving the Design of Existing Code”

    “Refactoring is a controlled technique for improving the design of an existing code base. Its essence is applying a series of small behavior-preserving transformations… the cumulative effect of each of these transformations is quite significant. By doing them in small steps you reduce the risk of introducing errors… [and] avoid having the system broken while you are carrying out the restructuring - which allows you to gradually refactor a system over an extended period of time.” Why Refactor?
  4. @fed_goose * Why Refactor? * * * * * *

    * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  5. @fed_goose <script src=“doAThing.js” /> <script src=“checkTheTime.js” /> <script src=“doABackflip.js” />

    <script src=“errorMessage.js” /> <script src=“thatOneTimeAtCodemash.js” /> → somePage.html
  6. @fed_goose <script src=“doAThing.js” /> <script src=“checkTheTime.js” /> <script src=“doABackflip.js” />

    <script src=“errorMessage.js” /> <script src=“thatOneTimeAtCodemash.js” /> → $element.on(‘click’, doTheTwoPageLongFunction); → somePage.html
  7. @fed_goose technical debt Why Refactor? inevitable rework that comes from

    choosing a quick and easy solution to a problem instead of spending time to write a more extensible solution
  8. @fed_goose –Joel Spolsky, “Things You Should Never Do, Part I”

    “Each of those bugs took weeks of real-world usage before they were found. The programmer might have spent a couple of days reproducing the bug in the lab and fixing it. If it’s like a lot of bugs, the fix might be one line of code, or it might even be a couple of characters, but a lot of work and time went into those two characters.” The Case Against Rewriting the Application
  9. @fed_goose –Joel Spolsky, “Things You Should Never Do, Part I”

    “You are wasting an outlandish amount of money writing code that already exists.”
  10. @fed_goose The Case Against Rewriting the Application 1. Application Complexity

    - Learn from your mistakes! 2. Maintenance & Resources 3. Customer Satisfaction & User Experience
  11. @fed_goose There are some things that rewrites can fix The

    Case Against Rewriting the Application For everyone else, there’s Refactoring
  12. @fed_goose Set Goals • Ease of on boarding developers? •

    Performance? • Reusability? • Maintainability? • Testability?
  13. @fed_goose Create a Plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan
  14. @fed_goose Create a Plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan } Research
  15. @fed_goose Create a plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan 4. Identify an opportunity 5. Start small & grow out over time
  16. @fed_goose Create a plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan 4. Identify an opportunity 5. Start small & grow out over time
  17. @fed_goose Why React? • Encapsulated • Declarative & Optimized •

    Testable • Documentation & Support • Domain Knowledge
  18. @fed_goose Create a plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan 4. Identify an opportunity 5. Start small & grow out over time
  19. @fed_goose Create a plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan 4. Identify an opportunity 5. Start small & grow out over time
  20. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head>…</head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  21. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head>…</head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  22. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head>…</head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  23. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head>…</head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  24. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head>…</head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  25. @fed_goose 1 // This exists to keep the checkbox &

    label in sync 2 var uniqueCounter = 0; 3 4 // template for the todo items 5 function todoCreator(todoText, completed) { 6 uniqueCounter = uniqueCounter + 1; 7 var todoId = 'todo' + uniqueCounter; 8 return '<li class="list-group-item d-flex">' + 9 ' <div class=“…" onchange="toggleComplete(this);maybeHideDeleteAll();">' + 10 ' <input type="checkbox" class="form-check-input" id="' + todoId + '">' + 11 ' <label class="form-check-label" for="' + todoId + '">' + 12 (completed ? '<del>' + todoText + '</del>' : todoText) + 13 ' </label>' + 14 ' </div>' + 15 ' <button onclick="deleteTodo(this);maybeHideDeleteAll();" class=“…”>Delete</button>' + 16 ‘</li>'; 17 } 18 19 // Add a todo to the list 20 function addTodo(todoText) { 21 var item = todoCreator(todoText); 22 $('#todos').append(item); 23 } 24 25 // Remove the todo entirely 26 function deleteTodo(todo) { 27 $(todo).parent().remove(); todo.js
  26. @fed_goose 1 // This exists to keep the checkbox &

    label in sync 2 var uniqueCounter = 0; 3 4 // template for the todo items 5 function todoCreator(todoText, completed) { 6 uniqueCounter = uniqueCounter + 1; 7 var todoId = 'todo' + uniqueCounter; 8 return '<li class="list-group-item d-flex">' + 9 ' <div class=“…" onchange="toggleComplete(this);maybeHideDeleteAll();">' + 10 ' <input type="checkbox" class="form-check-input" id="' + todoId + '">' + 11 ' <label class="form-check-label" for="' + todoId + '">' + 12 (completed ? '<del>' + todoText + '</del>' : todoText) + 13 ' </label>' + 14 ' </div>' + 15 ' <button onclick="deleteTodo(this);maybeHideDeleteAll();" class=“…”>Delete</button>' + 16 ‘</li>'; 17 } 18 19 // Add a todo to the list 20 function addTodo(todoText) { 21 var item = todoCreator(todoText); 22 $('#todos').append(item); 23 } 24 25 // Remove the todo entirely 26 function deleteTodo(todo) { 27 $(todo).parent().remove(); todo.js
  27. @fed_goose 12 (completed ? '<del>' + todoText + '</del>' :

    todoText) + 13 ' </label>' + 14 ' </div>' + 15 ' <button onclick="deleteTodo(this);maybeHideDeleteAll();" class=“…”>Delete</button>' + 16 ‘</li>'; 17 } 18 19 // Add a todo to the list 20 function addTodo(todoText) { 21 var item = todoCreator(todoText); 22 $('#todos').append(item); 23 } 24 25 // Remove the todo entirely 26 function deleteTodo(todo) { 27 $(todo).parent().remove(); 28 } 29 30 // Strike out the todo item when it is completed, remove strike when it is incomplete 31 function toggleComplete(todo) { 32 var label = $(todo).find('label'); 33 if ($(todo).find('input').prop('checked')) { 34 label.html('<del>' + 35 label.text() + 36 '</del>'); 37 } else { 38 label.html(label.text()); 39 } 40 } 41 42 function maybeHideDeleteAll() {
  28. @fed_goose 17 } 18 19 // Add a todo to

    the list 20 function addTodo(todoText) { 21 var item = todoCreator(todoText); 22 $('#todos').append(item); 23 } 24 25 // Remove the todo entirely 26 function deleteTodo(todo) { 27 $(todo).parent().remove(); 28 } 29 30 // Strike out the todo item when it is completed, remove strike when it is incomplete 31 function toggleComplete(todo) { 32 var label = $(todo).find('label'); 33 if ($(todo).find('input').prop('checked')) { 34 label.html('<del>' + 35 label.text() + 36 '</del>'); 37 } else { 38 label.html(label.text()); 39 } 40 } 41 42 function maybeHideDeleteAll() { 43 var completedItems = $('#todos input:checked').length; 44 if(completedItems > 0) { 45 $('#clearCompleted').show(); 46 } else { 47 $('#clearCompleted').hide(); 48 }
  29. @fed_goose 22 $('#todos').append(item); 23 } 24 25 // Remove the

    todo entirely 26 function deleteTodo(todo) { 27 $(todo).parent().remove(); 28 } 29 30 // Strike out the todo item when it is completed, remove strike when it is incomplete 31 function toggleComplete(todo) { 32 var label = $(todo).find('label'); 33 if ($(todo).find('input').prop('checked')) { 34 label.html('<del>' + 35 label.text() + 36 '</del>'); 37 } else { 38 label.html(label.text()); 39 } 40 } 41 42 function maybeHideDeleteAll() { 43 var completedItems = $('#todos input:checked').length; 44 if(completedItems > 0) { 45 $('#clearCompleted').show(); 46 } else { 47 $('#clearCompleted').hide(); 48 } 49 } 50 51 // Attach the DOM events once the page has loaded 52 $(document).ready(function() { 53 // When the form input is submitted, add the todo item
  30. @fed_goose 33 if ($(todo).find('input').prop('checked')) { 34 label.html('<del>' + 35 label.text()

    + 36 '</del>'); 37 } else { 38 label.html(label.text()); 39 } 40 } 41 42 function maybeHideDeleteAll() { 43 var completedItems = $('#todos input:checked').length; 44 if(completedItems > 0) { 45 $('#clearCompleted').show(); 46 } else { 47 $('#clearCompleted').hide(); 48 } 49 } 50 51 // Attach the DOM events once the page has loaded 52 $(document).ready(function() { 53 // When the form input is submitted, add the todo item 54 $("#addForm").on('submit', function(e) { 55 e.preventDefault(); 56 var input = $("input#todoInput"); 57 addTodo(input.val()); 58 input.val(""); 59 }); 60 61 // When Remove Completed button is clicked, remove all checked items from the DOM 62 $("#clearCompleted").on('click', function(e) { 63 e.preventDefault(e); 64 $('#todos input:checked').closest('li').remove();
  31. @fed_goose 43 var completedItems = $('#todos input:checked').length; 44 if(completedItems >

    0) { 45 $('#clearCompleted').show(); 46 } else { 47 $('#clearCompleted').hide(); 48 } 49 } 50 51 // Attach the DOM events once the page has loaded 52 $(document).ready(function() { 53 // When the form input is submitted, add the todo item 54 $("#addForm").on('submit', function(e) { 55 e.preventDefault(); 56 var input = $("input#todoInput"); 57 addTodo(input.val()); 58 input.val(""); 59 }); 60 61 // When Remove Completed button is clicked, remove all checked items from the DOM 62 $("#clearCompleted").on('click', function(e) { 63 e.preventDefault(e); 64 $('#todos input:checked').closest('li').remove(); 65 $(this).hide(); 66 }); 67 }); 68
  32. @fed_goose Header To Do Item To Do List Add Item

    Input Remove Completed Button
  33. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head></head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 </body> 25 </html> index.html
  34. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head></head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> 24 <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> 25 <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> 26 <script src="jquery/todo.js" type="text/javascript"></script> 27 </body> 28 </html> index.html
  35. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head></head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> 24 <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></ script> 25 <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> 26 <script src="jquery/todo.js" type="text/javascript"></script> 27 </body> 28 </html> index.html
  36. @fed_goose class AddTodoInput extends React.Component { render() { return (

    <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  37. @fed_goose class AddTodoInput extends React.Component { render() { return (

    <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  38. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head></head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <form class="form-inline d-inline" id="addForm"> 15 <input id="todoInput" class="form-control" type="text"/> 16 <button id="addTodo" type="submit" class=“…”>Add</button> 17 </form> 18 <button id="clearCompleted" class=“…” style="display: none"> 19 Remove Completed 20 </button> 21 </div> 22 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 23 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> 24 <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> 25 <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> 26 <script src="jquery/todo.js" type="text/javascript"></script> 27 </body> 28 </html> index.html
  39. @fed_goose 1 <!DOCTYPE html> 2 <html lang="en"> 3-8 <head></head> 9

    <body> 10 <div class="container"> 11 <h1>To Do List</h1> 12 <ul id="todos" class="list-group my-2"> 13 </ul> 14 <span data-react-component="AddTodoInput"></span> 15 <button id="clearCompleted" class=“…” style="display: none"> 16 Remove Completed 17 </button> 18 </div> 19 <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script> 20 <script src="https://unpkg.com/react@16/umd/react.development.js"></script> 21 <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script> 22 <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script> 23 <script src="jquery/todo.js" type="text/javascript"></script> 24 <script src="components/AddTodoInput.js" type="text/babel"></script> 25 </body> 26 </html> index.html
  40. @fed_goose class AddTodoInput extends React.Component { render() { return (

    <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js ReactDOM.render( <AddTodoInput/>, document.querySelector(‘[data-react-component="AddTodoInput"]') );
  41. @fed_goose $("#addForm").on('submit', function(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val());

    input.val(""); }); todo.js class AddTodoInput extends React.Component { render() { return ( <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  42. @fed_goose $("#addForm").on('submit', function(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val());

    input.val(""); }); todo.js class AddTodoInput extends React.Component { render() { return ( <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  43. @fed_goose $("#addForm").on('submit', function(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val());

    input.val(""); }); todo.js class AddTodoInput extends React.Component { render() { return ( <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  44. @fed_goose class AddTodoInput extends React.Component { render() { return (

    <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); AddTodoInput.js
  45. @fed_goose class AddTodoInput extends React.Component { handleSubmit(e) { } render()

    { return ( <form className="form-inline d-inline" id="addForm"> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); AddTodoInput.js
  46. @fed_goose class AddTodoInput extends React.Component { handleSubmit(e) { e.preventDefault(); var

    input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  47. @fed_goose class AddTodoInput extends React.Component { handleSubmit(e) { e.preventDefault(); var

    input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  48. @fed_goose class AddTodoInput extends React.Component { handleSubmit(e) { e.preventDefault(); var

    input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text"/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  49. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  50. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  51. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  52. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); var input = $("input#todoInput"); addTodo(input.val()); input.val(""); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className=“…”>Add</button> </form> ); } } AddTodoInput.js
  53. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } } AddTodoInput.js
  54. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } } AddTodoInput.js
  55. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text}/> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } } AddTodoInput.js
  56. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } handleInput(e) { this.setState({ text: e.target.value }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text} onChange={this.handleInput} /> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } } AddTodoInput.js
  57. @fed_goose class AddTodoInput extends React.Component { constructor(props) { super(props); this.state

    = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } handleInput(e) { this.setState({ text: e.target.value }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text} onChange={this.handleInput} /> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } } AddTodoInput.js
  58. @fed_goose AddTodoInput.js class AddTodoInput extends React.Component { constructor(props) { super(props);

    this.state = { text: '' }; } handleSubmit(e) { e.preventDefault(); addTodo(this.state.text); this.setState({ text: '' }); } handleInput(e) { this.setState({ text: e.target.value }); } render() { return ( <form className="form-inline d-inline" id="addForm" onSubmit={this.handleSubmit}> <input id="todoInput" className="form-control" type="text" value={this.state.text} onChange={this.handleInput} /> <button id="addTodo" type="submit" className="…">Add</button> </form> ); } }
  59. @fed_goose class RemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button id=“clearCompleted" className=“…" onClick={removeCheckedItems}> Remove Completed </button> ); } return null; } } RemoveCompletedButtons.js
  60. @fed_goose class RemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button id=“clearCompleted" className=“…" onClick={removeCheckedItems}> Remove Completed </button> ); } return null; } } RemoveCompletedButtons.js
  61. @fed_goose class RemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button id=“clearCompleted" className=“…" onClick={removeCheckedItems}> Remove Completed </button> ); } return null; } } RemoveCompletedButtons.js
  62. 3. Changes to state happen in pure functions Redux Principles

    function reducer(state = {}, action) { if (action.type = 'SOMETHING_HAPPENED') { return { moreInformation: action.moreInformation }; } return state; }
  63. @fed_goose Redux Principles 1. Single source of truth 2. State

    is read-only 3. Changes to state happen in pure functions
  64. @fed_goose class RemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button id=“clearCompleted" className=“…" onClick={removeCheckedItems}> Remove Completed </button> ); } return null; } } RemoveCompletedButtons.js
  65. @fed_goose class RemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button id=“clearCompleted" className=“…" onClick={removeCheckedItems}> Remove Completed </button> ); } return null; } } RemoveCompletedButtons.js
  66. @fed_goose // The initial state of our store const initialState

    = { hasCompletedItems: false }; Initial state
  67. @fed_goose const initialState = { hasCompletedItems: false }; function reducer(state

    = initialState, action) { if (action.type = 'SET_HAS_COMPLETED_ITEMS') { return { hasCompletedItems: action.hasCompletedItems }; } return state; } reducer
  68. @fed_goose // Create the store with the reducer var store

    = Redux.createStore(reducer); Create the Store
  69. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  70. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  71. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  72. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  73. @fed_goose const initialState = { hasCompletedItems: false }; function reducer(state

    = initialState, action) { if (action.type = 'SET_HAS_COMPLETED_ITEMS') { return { hasCompletedItems: action.hasCompletedItems }; } return state; } reducer
  74. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  75. @fed_goose class UnconnectedRemoveCompletedButton extends React.Component { render() { if(this.props.show) {

    return ( <button onClick={removeCheckedItems} className="…"> Remove Completed </button> ); } return null; } } function mapStateToProps(state) { return { show: state.hasCompletedItems }; } var RemoveCompletedButton = ReactRedux.connect(mapStateToProps) (UnconnectedRemoveCompletedButton); ReactDOM.render( <RemoveCompletedButton store={store} />, document.querySelector('[data-react-component="RemoveCompletedButton"]') );
  76. @fed_goose Redux Principles 1. Single source of truth 2. State

    is read-only 3. Changes to state happen in pure functions
  77. @fed_goose Why Redux? • Boot from state persisted in browser

    local storage. • Trace user interactions • Maintain undo history • Optimistic updates • Robust development tooling • Encapsulated business logic
  78. @fed_goose Create a plan 1. Choose a technology 2. Try

    it out, find a pattern 3. Review & plan 4. Identify an opportunity 5. Start small & grow out over time
  79. @fed_goose Sources “Things You Should Never Do, Part I”, Joel

    Spolsky, https://www.joelonsoftware.com/2000/04/06/things- you-should-never-do-part-i/ “Refactoring: Improving the Design of Existing Code”, Martin Fowler “Refactor or Rewrite”, Marco, Rails Love, https://www.railslove.com/stories/refactor-or-rewrite “How To Work With Legacy Code Management Summary”, Marco https://speakerdeck.com/donschado/ how-to-work-with-legacy-code-management-summary “Don’t Rewrite, React”, Ryan Florence, React-Europe 2015, https://www.youtube.com/watch? v=BF58ZJ1ZQxY “Motivation”, Redux Documentation, https://redux.js.org/docs/introduction/Motivation.html “You Might not need Redux”, Dan Abramov, https://medium.com/@dan_abramov/you-might-not-need- redux-be46360cf367