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

Refactoring a Legacy Application with React

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.

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/[email protected]/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/[email protected]/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/[email protected]/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/[email protected]/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