Advertisement
Guest User

Untitled

a guest
Aug 24th, 2019
65
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 4.93 KB | None | 0 0
  1. import React, { useEffect, useRef } from "react"
  2. import useMethods from "use-methods"
  3.  
  4. const setLocalStorage = (key: string, value: Object) => {
  5. const json = JSON.stringify(value)
  6. localStorage.setItem(key, json)
  7. }
  8.  
  9. const getLocalStorage = (key: string) => {
  10. const json = localStorage.getItem(key)
  11. if (json === null) {
  12. return null
  13. }
  14. return JSON.parse(json)
  15. }
  16.  
  17. const shortID = () => {
  18. // See gist.github.com/gordonbrander/2230317 for reference.
  19. return Math.random().toString(36).substr(2, 4)
  20. }
  21.  
  22. type State = {
  23. focus: boolean,
  24. input: string,
  25. todos: {
  26. id: string,
  27. value: string,
  28. complete: boolean
  29. }[],
  30. saved: boolean,
  31.  
  32. // The undo/redo state stack and index.
  33. stack: [],
  34. index: 0
  35. }
  36.  
  37. const initialState: State = {
  38. focus: false,
  39. input: "",
  40. todos: [],
  41. saved: true,
  42.  
  43. // The udno/redo state stack and index.
  44. stack: [],
  45. index: 0
  46. }
  47.  
  48. // selected
  49. // undo
  50. // redo
  51.  
  52. // if (todo.complete) {
  53. // state.count.push(todo.id)
  54. // } else {
  55. // const countIndex = state.count.findIndex(cmpTodo => todo.id == cmpTodo.id)
  56. // if (countIndex !== -1) {
  57. // state.count.splice(countIndex, 1)
  58. // }
  59. // }
  60.  
  61. const methods = (state: State) => ({
  62. clearAll() {
  63. return initialState
  64. },
  65. focus() {
  66. state.focus = true
  67. },
  68. blur() {
  69. state.focus = false
  70. },
  71. input(value: string) {
  72. state.input = value
  73. },
  74. addTodo() {
  75. if (state.focus && state.input) {
  76. state.todos.unshift({id: shortID(), value: state.input, complete: false})
  77. state.input = ""
  78. }
  79. },
  80. toggleTodo(id: string) {
  81. const todo = state.todos.find(todo => id === todo.id)
  82. if (todo) {
  83. todo.complete = !todo.complete
  84. }
  85. },
  86. updateTodo(id: string, value: string) {
  87. const todo = state.todos.find(todo => id === todo.id)
  88. if (todo) {
  89. todo.value = value
  90. }
  91. },
  92. deleteTodo(id: string) {
  93. const index = state.todos.findIndex(todo => id === todo.id)
  94. if (index !== -1) {
  95. state.todos.splice(index, 1)
  96. }
  97. },
  98. deleteAllTodos() {
  99. // Use `splice` backwards to avoid destructive resizing.
  100. // See djave.co.uk/blog/read/splice-doesnt-work-very-well-in-a-javascript-for-loop for reference.
  101. for (let index = state.todos.length; index >= 0; index--) {
  102. const todo = state.todos[index]
  103. if (todo && todo.complete) {
  104. state.todos.splice(index, 1)
  105. }
  106. }
  107. },
  108. saving() {
  109. state.saved = false
  110. },
  111. saved() {
  112. state.saved = true
  113. },
  114. undo() {
  115.  
  116. },
  117. redo() {
  118.  
  119. }
  120. })
  121.  
  122. // See github.com/streamich/react-use/blob/master/src/useUpdateEffect.ts for reference.
  123. const useUpdateEffect: typeof useEffect = (effect, deps) => {
  124. const didEffect = useRef(false)
  125. useEffect(
  126. !didEffect.current
  127. ? () => {
  128. didEffect.current = true
  129. }
  130. : effect,
  131. deps
  132. )
  133. }
  134.  
  135. // See github.com/streamich/react-use/blob/master/src/useDebounce.ts for reference.
  136. const useDebounce = (fn: () => any, ms: number = 0, deps: any[] = []) => {
  137. useUpdateEffect(() => {
  138. const debounce = setTimeout(fn.bind(null, deps), ms)
  139. return () => {
  140. clearTimeout(debounce)
  141. }
  142. }, deps)
  143. }
  144.  
  145. const App = (props: { children?: any }) => {
  146. const [state, dispatch] = useMethods(methods, getLocalStorage("todo-app") || initialState)
  147.  
  148. const stateRef = useRef(state)
  149. stateRef.current = state
  150.  
  151. useUpdateEffect(() => {
  152. dispatch.saving()
  153. }, [state.input, state.todos])
  154.  
  155. useDebounce(() => {
  156. dispatch.saved()
  157. setLocalStorage("todo-app", stateRef.current)
  158. }, 1e3, [state.input, state.todos])
  159.  
  160. const handleClear = (e: React.FormEvent<EventTarget>) => {
  161. e.preventDefault()
  162. const sure = window.confirm("Are you sure?")
  163. if (sure) {
  164. dispatch.clearAll()
  165. }
  166. }
  167.  
  168. const handleSubmit = (e: React.FormEvent<EventTarget>) => {
  169. e.preventDefault()
  170. dispatch.addTodo()
  171. }
  172.  
  173. const completed = state.todos.reduce((arr, todo, index) => {
  174. if (todo.complete) {
  175. arr.push(index)
  176. }
  177. return arr
  178. }, [] as number[]) // TS requires `as number[]`.
  179. // const a = state.todos.reduce((arr, todo, index) => {
  180. // if (todo.complete) {
  181. // arr.push(index)
  182. // }
  183. // return arr
  184. // }, ([]: number[]))
  185.  
  186. return (
  187. <div>
  188. <form onSubmit={handleSubmit}>
  189. {/* Use `<input type="button" />` instead of <button type="submit" />. */}
  190. <input type="button" value="Clear all" onClick={handleClear} />
  191. <input type="text" value={state.input} onFocus={dispatch.focus} onChange={e => dispatch.input(e.target.value)} />
  192. <button type="submit">Add</button>
  193. <input type="button" value={"Delete all" + (!completed.length ? "" : " " + completed.length)} onClick={dispatch.deleteAllTodos} />
  194. Saved status: {"" + state.saved}
  195. {state.todos && (
  196. <ul>
  197. {/* {all.length} */}
  198. {state.todos.map(({id, value, complete}) => (
  199. <li key={id}>
  200. <input type="checkbox" checked={complete} onChange={() => dispatch.toggleTodo(id)} />
  201. <input style={{textDecoration: !complete ? "none" : "line-through"}} type="text" value={value} onFocus={dispatch.blur} onChange={e => dispatch.updateTodo(id, e.target.value)} />
  202. <button onClick={() => dispatch.deleteTodo(id)}>Delete</button>
  203. </li>
  204. ))}
  205. </ul>
  206. )}
  207. </form>
  208. </div>
  209. )
  210. }
  211.  
  212. export default App
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement