Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

JS Fest 2019. Espen Hovlandsdal. Creating collaborative editing UIs in React

37 views

Published on

While many developers have created an “admin UI” or editing interface at some point or other, they are rarely built in a way that allows them to work in a real-time, collaborative way as we are getting used to with products such as Google Docs. In this talk, we’re going to discuss some of the challenges of building real-time interfaces and different ways to approach and solve them.

Published in: Education
  • Be the first to comment

  • Be the first to like this

JS Fest 2019. Espen Hovlandsdal. Creating collaborative editing UIs in React

  1. 1. Creating collaborative editing UIs (in React)
  2. 2. Photo by Roman Kraft on Unsplash
  3. 3. Photo by Kelly Sikkema on Unsplash We’ll be back with more!
  4. 4. Espen Hovlandsdal Full-stack developer @
  5. 5. That story was just fiction. But there is still a lesson to be learned.
  6. 6. The web was always built for collaboration
  7. 7. The web was originally developed for automated information-sharing between scientists in universities and institutes around the world.
  8. 8. ●Upload HTML ●Share URL
  9. 9.
  10. 10. Are we there yet?
  11. 11. Photo by Suhyeon Choi on Unsplash
  12. 12. Photo by A L L E F . V I N I C I U S Δ on Unsplash
  13. 13. Truly collaborative services: the next evolution?
  14. 14. How do you create collaborative UIs?
  15. 15. Real-time is hard, let’s look at building async collaboration.
  16. 16. Read, modify, write. Check out, edit, commit. GET, modify, PUT.
  17. 17. Most editing UIs are multi-user UIs. Photo by Brooke Cagle on Unsplash
  18. 18. Easy, right?
  19. 19. 50% 50%
  20. 20. Photo by CMDR Shane on Unsplash Locking: Optimistic vs pessimistic
  21. 21. Option 1
  22. 22. Photo by Nathan Dumlao on Unsplash
  23. 23. ● Most annoying time to be informed of conflict ● Do you try to resolve? ● Theirs? Ours? Optimistic locking
  24. 24. Option 2
  25. 25. Photo by Jon Butterworth on Unsplash Pessimistic locking
  26. 26. ● When do you unlock? ● “Takeover”-feature? ● Fine-grained locking? Pessimistic locking
  27. 27. Pessimistic locking
  28. 28. Option 3
  29. 29. Photo by novia wu on Unsplash Pretending it’s not a problem
  30. 30. Maybe we can resolve conflicts?
  31. 31. Photo by Jennifer Regnier on Unsplash
  32. 32. Maybe real-time isn’t that much harder?
  33. 33. Photo by James Owen on Unsplash
  34. 34. Conflict resolution: Real-time vs deferred Photo by Veri Ivanova on Unsplash
  35. 35. Real-time, collaborative UIs are better for everyone
  36. 36. ...but you might need a different backend
  37. 37. Photo by Daniel McCullough on Unsplash Let’s build.
  38. 38. Photo by Esther Jiao on Unsplash Real-time transport State management Patches / mutations Conflict resolution Latency masking UI concerns
  39. 39. Photo by Jack Anstey on Unsplash Transports
  40. 40. State Photo by Liam Briese on Unsplash
  41. 41. Patches Photo by me (can you tell?)
  42. 42. This is the 1st line. Donuts are awesome. This is the first line. Waffles are awesome. This is the 1st line. Donuts are awesome. This is the first line. Waffles are awesome. This is the 1first line. DonutWaffles are awesome.
  43. 43. Conflict resolution Photo by Jack Sharp on Unsplash
  44. 44. Photo by Veri Ivanova on Unsplash Latency masking
  45. 45. UI concerns Photo by José Alejandro Cuffia on Unsplash
  46. 46. Photo by Alexandru Zdrobău on Unsplash Presence
  47. 47. The robots are coming Photo by Rock'n Roll Monkey on Unsplash
  48. 48. Where does React tie in?
  49. 49. class MyInput extends Component { handleChange = evt => { this.props.onChange(evt.target.value) } render() { return ( <input onChange={this.handleChange} value={this.props.value} /> ) } } Container handles patching/propagation
  50. 50. handleChange = evt => { this._didEdit = true this.props.onChange(evt.target.value) } getSnapshotBeforeUpdate(prev) { const changed = prev.value !== this.props.value return !changed || this._didEdit ? null : this.getCaretPosition() } “Your” changes vs “their” changes
  51. 51. componentDidUpdate(props, state, snapshot) { this._didEdit = false if (snapshot) { this.restoreCaretPosition(snapshot) } } restoreCaretPosition(snapshot) { const el = this._inputRef.current el.selectionStart = snapshot.start el.selectionEnd = snapshot.end } Applying caret position snapshot
  52. 52. "This works pretty| well, actually" { text: "etty wel", cursorDiff: 4, searchFrom: 13, length: 0, index: 17 } Where to place caret? (get snapshot)
  53. 53. const newIndex = dmp.match_main(value, text, searchFrom) const found = newIndex !== -1 el.selectionStart = found ? newIndex + cursorDiff : oldIndex el.selectionEnd = found ? newIndex + cursorDiff + length : oldIndex + length Where to place caret? (restore snapshot)
  54. 54. 👀Presence
  55. 55. ● Roll-call ● Client check-in ● Granular “path”
  56. 56. Explicit log-off navigator.sendBeacon(url, data)
  57. 57. “Google Docs”- style carets
  58. 58. Photo by Kelly Sikkema on Unsplash We have the pieces
  59. 59. Photo by Susan Holt Simpson on Unsplash We need bigger building blocks
  60. 60. Photo by Susan Holt Simpson on Unsplash Let’s start building them!
  61. 61. Thanks! Espen Hovlandsdal twitter.com/rexxars github.com/rexxars

×