Skip to content

Instantly share code, notes, and snippets.

@brandonchadlange
Created October 3, 2022 20:54
Show Gist options
  • Save brandonchadlange/4fc268600d1636b80bb926e4e8ee82fc to your computer and use it in GitHub Desktop.
Save brandonchadlange/4fc268600d1636b80bb926e4e8ee82fc to your computer and use it in GitHub Desktop.
Todos full implementation
interface Todo {
description: string;
complete: boolean;
}
const useTodoListViewModel = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const createTodo = (description: string) => {
const newTodo: Todo = {
description,
complete: false,
};
setTodos((current) => [...current, newTodo]);
};
return {
todos,
createTodo,
};
};
const TodoList = ({ todos }: { todos: Todo[] }) => {
const todoList = todos.map((todo) => <Todo todo={todo} />);
return <ul>{todoList}</ul>;
};
const useTodoViewModel = (todo: Todo) => {
const [checked, setChecked] = useState(todo.complete);
const textDecoration = {
textDecoration: todo.complete ? "line-through" : "none",
};
const toggleChecked = () => {
setChecked((checked) => !checked);
};
return {
description: todo.description,
checked,
textDecoration,
toggleChecked,
};
};
const Todo = ({ todo }: { todo: Todo }) => {
const vm = useTodoViewModel(todo);
return (
<li>
<label>
<input
defaultChecked={vm.checked}
onChange={vm.toggleChecked}
type="checkbox"
/>
<span style={{ ...vm.textDecoration }}>{vm.description}</span>
</label>
</li>
);
};
const useAddTodoVm = (onAdd: (description: string) => void) => {
const [inputText, setInputText] = useState("");
const addTodo = () => {
onAdd(inputText);
setInputText("");
};
return {
inputText,
setInputText,
addTodo,
};
};
interface AddTodoViewModel {
inputText: string;
setInputText: (inputText: string) => void;
addTodo: () => void;
}
const AddTodo = ({ viewModel }: { viewModel: AddTodoViewModel }) => {
return (
<>
<input
value={viewModel.inputText}
onChange={(e) => viewModel.setInputText(e.target.value)}
type="text"
placeholder="Add todo"
/>
<button onClick={viewModel.addTodo}>Add</button>
</>
);
};
const Todos = () => {
const listVm = useTodoListViewModel();
const addTodoVm = useAddTodoVm(listVm.createTodo);
return (
<>
<TodoList todos={listVm.todos} />
<AddTodo viewModel={addTodoVm} />
</>
);
};
export default Todos;
@yogithesymbian
Copy link

1. todos.tsx
2. add-todo.tsx 
3. todo-list.tsx 
4. todo.tsx 
5. todo-edit-item.tsx

@yogithesymbian
Copy link

yogithesymbian commented Jun 26, 2024

@brandonchadlange how about these

pattern

INTERFACE

// todo/interface/list.tsx

interface Todo {
  description: string;
  complete: boolean;
}
// todo/interface/add.tsx

interface AddTodoViewModel {
  inputText: string;
  setInputText: (inputText: string) => void;
  addTodo: () => void;
}

VIEW MODEL

// todo/view-model/view-model.tsx

const useTodoViewModel = (todo: Todo) => {
  const [checked, setChecked] = useState(todo.complete);
  const textDecoration = {
    textDecoration: todo.complete ? "line-through" : "none",
  };
  const toggleChecked = () => {
    setChecked((checked) => !checked);
  };

  return {
    description: todo.description,
    checked,
    textDecoration,
    toggleChecked,
  };
};


const useTodoListViewModel = () => {
  const [todos, setTodos] = useState<Todo[]>([]);

  const createTodo = (description: string) => {
    const newTodo: Todo = {
      description,
      complete: false,
    };

    setTodos((current) => [...current, newTodo]);
  };

  return {
    todos,
    createTodo,
  };
};


const useAddTodoVm = (onAdd: (description: string) => void) => {
  const [inputText, setInputText] = useState("");

  const addTodo = () => {
    onAdd(inputText);
    setInputText("");
  };

  return {
    inputText,
    setInputText,
    addTodo,
  };
};

component

// todo/component/todo-list.tsx

const Todo = ({ todo }: { todo: Todo }) => {
  const vm = useTodoViewModel(todo);

  return (
    <li>
      <label>
        <input
          defaultChecked={vm.checked}
          onChange={vm.toggleChecked}
          type="checkbox"
        />
        <span style={{ ...vm.textDecoration }}>{vm.description}</span>
      </label>
    </li>
  );
};

const TodoList = ({ todos }: { todos: Todo[] }) => {
  const todoList = todos.map((todo) => <Todo todo={todo} />);

  return <ul>{todoList}</ul>;
};
// todo/component/add-todo.tsx
const AddTodo = ({ viewModel }: { viewModel: AddTodoViewModel }) => {
  return (
    <>
      <input
        value={viewModel.inputText}
        onChange={(e) => viewModel.setInputText(e.target.value)}
        type="text"
        placeholder="Add todo"
      />
      <button onClick={viewModel.addTodo}>Add</button>
    </>
  );
};
// todo/page.tsx

const Todos = () => {
  const listVm = useTodoListViewModel();
  const addTodoVm = useAddTodoVm(listVm.createTodo);

  return (
    <>
      <TodoList todos={listVm.todos} />
      <AddTodo viewModel={addTodoVm} />
    </>
  );
};

export default Todos;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment