TodoMVC
This is an example of real-time collaborative TodoMVC using CreateReactApp and Yorkie JS SDK.
App.tsx
1import React, { useState, useEffect } from 'react';2import yorkie, { Document, JSONArray } from 'yorkie-js-sdk';3import 'todomvc-app-css/index.css';45import Header from './Header';6import MainSection from './MainSection';7import { Todo } from './model';8import './App.css';910const initialState = [11 {12 id: 0,13 text: 'Yorkie JS SDK',14 completed: false,15 },16 {17 id: 1,18 text: 'Garbage collection',19 completed: false,20 },21 {22 id: 2,23 text: 'RichText datatype',24 completed: false,25 },26] as Array<Todo>;2728/**29 * `App` is the root component of the application.30 */31export default function App() {32 const [doc] = useState<Document<{ todos: JSONArray<Todo> }>>(33 () =>34 new yorkie.Document<{ todos: JSONArray<Todo> }>(35 `react-todomvc-${new Date()36 .toISOString()37 .substring(0, 10)38 .replace(/-/g, '')}`,39 ),40 );41 const [todos, setTodos] = useState<Array<Todo>>([]);4243 const actions = {44 addTodo: (text: string) => {45 doc?.update((root) => {46 root.todos.push({47 id:48 root.todos.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) +49 1,50 completed: false,51 text,52 });53 });54 },55 deleteTodo: (id: number) => {56 doc?.update((root) => {57 let target;58 for (const todo of root.todos) {59 if (todo.id === id) {60 target = todo as any;61 break;62 }63 }64 if (target) {65 root.todos.deleteByID!(target.getID());66 }67 });68 },69 editTodo: (id: number, text: string) => {70 doc?.update((root) => {71 let target;72 for (const todo of root.todos) {73 if (todo.id === id) {74 target = todo;75 break;76 }77 }78 if (target) {79 target.text = text;80 }81 });82 },83 completeTodo: (id: number) => {84 doc?.update((root) => {85 let target;86 for (const todo of root.todos) {87 if (todo.id === id) {88 target = todo;89 break;90 }91 }92 if (target) {93 target.completed = !target.completed;94 }95 });96 },97 clearCompleted: () => {98 doc?.update((root) => {99 for (const todo of root.todos) {100 if (todo.completed) {101 const t = todo as any;102 root.todos.deleteByID!(t.getID());103 }104 }105 }, '');106 },107 };108109 useEffect(() => {110 const client = new yorkie.Client(import.meta.env.VITE_YORKIE_API_ADDR, {111 apiKey: import.meta.env.VITE_YORKIE_API_KEY,112 });113114 /**115 * `attachDoc` is a helper function to attach the document into the client.116 */117 async function attachDoc(118 doc: Document<{ todos: JSONArray<Todo> }>,119 callback: (todos: any) => void,120 ) {121 // 01. create client with RPCAddr then activate it.122 await client.activate();123124 // 02. attach the document into the client.125 await client.attach(doc);126127 // 03. create default todos if not exists.128 doc.update((root) => {129 if (!root.todos) {130 root.todos = initialState;131 }132 }, 'create default todos if not exists');133134 // 04. subscribe change event from local and remote.135 doc.subscribe((event) => {136 callback(doc.getRoot().todos);137 });138139 // 05. set todos the attached document.140 callback(doc.getRoot().todos);141 }142143 attachDoc(doc, (todos) => {144 setTodos(todos);145 });146 }, []);147148 return (149 <div className="App">150 <Header addTodo={actions.addTodo} />151 <MainSection todos={todos} actions={actions} />152 </div>153 );154}
- User 1
- User 2
- User 1
- User 2
Event Log