Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import React, { useEffect, useRef } from "react"
- import useMethods from "use-methods"
- const setLocalStorage = (key: string, value: Object) => {
- const json = JSON.stringify(value)
- localStorage.setItem(key, json)
- }
- const getLocalStorage = (key: string) => {
- const json = localStorage.getItem(key)
- if (json === null) {
- return null
- }
- return JSON.parse(json)
- }
- const shortID = () => {
- // See gist.github.com/gordonbrander/2230317 for reference.
- return Math.random().toString(36).substr(2, 4)
- }
- type State = {
- focus: boolean,
- input: string,
- todos: {
- id: string,
- value: string,
- complete: boolean
- }[],
- saved: boolean,
- // The undo/redo state stack and index.
- stack: [],
- index: 0
- }
- const initialState: State = {
- focus: false,
- input: "",
- todos: [],
- saved: true,
- // The udno/redo state stack and index.
- stack: [],
- index: 0
- }
- // selected
- // undo
- // redo
- // if (todo.complete) {
- // state.count.push(todo.id)
- // } else {
- // const countIndex = state.count.findIndex(cmpTodo => todo.id == cmpTodo.id)
- // if (countIndex !== -1) {
- // state.count.splice(countIndex, 1)
- // }
- // }
- const methods = (state: State) => ({
- clearAll() {
- return initialState
- },
- focus() {
- state.focus = true
- },
- blur() {
- state.focus = false
- },
- input(value: string) {
- state.input = value
- },
- addTodo() {
- if (state.focus && state.input) {
- state.todos.unshift({id: shortID(), value: state.input, complete: false})
- state.input = ""
- }
- },
- toggleTodo(id: string) {
- const todo = state.todos.find(todo => id === todo.id)
- if (todo) {
- todo.complete = !todo.complete
- }
- },
- updateTodo(id: string, value: string) {
- const todo = state.todos.find(todo => id === todo.id)
- if (todo) {
- todo.value = value
- }
- },
- deleteTodo(id: string) {
- const index = state.todos.findIndex(todo => id === todo.id)
- if (index !== -1) {
- state.todos.splice(index, 1)
- }
- },
- deleteAllTodos() {
- // Use `splice` backwards to avoid destructive resizing.
- // See djave.co.uk/blog/read/splice-doesnt-work-very-well-in-a-javascript-for-loop for reference.
- for (let index = state.todos.length; index >= 0; index--) {
- const todo = state.todos[index]
- if (todo && todo.complete) {
- state.todos.splice(index, 1)
- }
- }
- },
- saving() {
- state.saved = false
- },
- saved() {
- state.saved = true
- },
- undo() {
- },
- redo() {
- }
- })
- // See github.com/streamich/react-use/blob/master/src/useUpdateEffect.ts for reference.
- const useUpdateEffect: typeof useEffect = (effect, deps) => {
- const didEffect = useRef(false)
- useEffect(
- !didEffect.current
- ? () => {
- didEffect.current = true
- }
- : effect,
- deps
- )
- }
- // See github.com/streamich/react-use/blob/master/src/useDebounce.ts for reference.
- const useDebounce = (fn: () => any, ms: number = 0, deps: any[] = []) => {
- useUpdateEffect(() => {
- const debounce = setTimeout(fn.bind(null, deps), ms)
- return () => {
- clearTimeout(debounce)
- }
- }, deps)
- }
- const App = (props: { children?: any }) => {
- const [state, dispatch] = useMethods(methods, getLocalStorage("todo-app") || initialState)
- const stateRef = useRef(state)
- stateRef.current = state
- useUpdateEffect(() => {
- dispatch.saving()
- }, [state.input, state.todos])
- useDebounce(() => {
- dispatch.saved()
- setLocalStorage("todo-app", stateRef.current)
- }, 1e3, [state.input, state.todos])
- const handleClear = (e: React.FormEvent<EventTarget>) => {
- e.preventDefault()
- const sure = window.confirm("Are you sure?")
- if (sure) {
- dispatch.clearAll()
- }
- }
- const handleSubmit = (e: React.FormEvent<EventTarget>) => {
- e.preventDefault()
- dispatch.addTodo()
- }
- const completed = state.todos.reduce((arr, todo, index) => {
- if (todo.complete) {
- arr.push(index)
- }
- return arr
- }, [] as number[]) // TS requires `as number[]`.
- // const a = state.todos.reduce((arr, todo, index) => {
- // if (todo.complete) {
- // arr.push(index)
- // }
- // return arr
- // }, ([]: number[]))
- return (
- <div>
- <form onSubmit={handleSubmit}>
- {/* Use `<input type="button" />` instead of <button type="submit" />. */}
- <input type="button" value="Clear all" onClick={handleClear} />
- <input type="text" value={state.input} onFocus={dispatch.focus} onChange={e => dispatch.input(e.target.value)} />
- <button type="submit">Add</button>
- <input type="button" value={"Delete all" + (!completed.length ? "" : " " + completed.length)} onClick={dispatch.deleteAllTodos} />
- Saved status: {"" + state.saved}
- {state.todos && (
- <ul>
- {/* {all.length} */}
- {state.todos.map(({id, value, complete}) => (
- <li key={id}>
- <input type="checkbox" checked={complete} onChange={() => dispatch.toggleTodo(id)} />
- <input style={{textDecoration: !complete ? "none" : "line-through"}} type="text" value={value} onFocus={dispatch.blur} onChange={e => dispatch.updateTodo(id, e.target.value)} />
- <button onClick={() => dispatch.deleteTodo(id)}>Delete</button>
- </li>
- ))}
- </ul>
- )}
- </form>
- </div>
- )
- }
- export default App
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement