Duration: Estimated 15 minutes

For: Developers who are tired of Redux

Code: Warehouse

React 16.13 + TypeScript 3.7 + Mobx 5.15 + mobx-React 6.1

Get a better reading experience


Goal:

Implement a Todo List application as follows

Step 1️ : Create a Store Model

// ./src/stores/todo.ts
import { action, observable, computed } from 'mobx';

export interface ITodo {
  id: number;
  name: string;
  desc: string; done? :boolean;
}

let id = 0;

export class TodoStore {
  @observable todos: ITodo[] = [];

  // Count the number of incomplete completions using the compute attribute
  @computed get doneCount() {
    return this.todos.filter(todo= > todo.done).length;
  }

  @computed get undoneCount() {
    return this.todos.filter(todo= >! todo.done).length; }// Add a Todo
  @action.bound addNewTodo() {
    const i = id++;
    const todo = {
      name: 'new task' + i,
      desc: 'new task' + i,
      id: i,
      done: false};this.todos = [...this.todos, todo];
  }

  // Delete a Todo
  @action.bound removeById(id: number) {
    this.todos = this.todos.filter(todo= >todo.id ! == id); }// Switch to done
  @action.bound toggleStatusById(id: number) {
    this.todos = this.todos.map(todo= > {
      if(todo.id === id) { todo.done = ! todo.done; }returntodo; }); }}export const STORE_TODO = 'todoStore';
Copy the code

Step 2️ : Export Store

// ./src/stores/index.ts
import { createContext, useContext } from 'react';
import { STORE_TODO, TodoStore } from './todo';

function createStores() {
  return {
    [STORE_TODO]: new TodoStore(),
  };
}

const stores = createStores();

const StoresContext = createContext(stores);

/ / hooks to use notes to see here - > https://github.com/olivewind/blog/issues/1
const useStores = (a)= > useContext(StoresContext);

function useTodoStore() {
  const { todoStore } = useStores();
  return todoStore;
}

export {
  stores,
  useTodoStore
};
Copy the code

Step 3️ : Usemobx-reactBind the Store to the component

// ./src/app.tsx import React from 'react'; import { Provider } from 'mobx-react'; import Routers from './containers/routers'; import { stores, StoresContext } from './stores'; Function App() {return (// Provider <Provider {... Stores}> {/* Service function components */} <StoresContext.Provider value={stores}> < storesContext. Provider> </Provider>) } export default App;Copy the code

Step 4️ : Use state in function components

// ./src/containers/todo-list-fn/index.tsx import React from 'react'; import { observer } from 'mobx-react'; import { useTodoStore } from '.. /.. /stores'; import { Todo } from '.. /.. /components'; function TodoListFnPage() { const { todos, undoneCount, doneCount, addNewTodo, removeById, toggleStatusById } = useTodoStore(); return ( <div> <div> Done: {doneCount} Undone: {undoneCount} </div> <br /> { todos.map((todo) => { return ( <Todo key={todo.id} todo={todo} onRemove={removeById} switchStatus={toggleStatusById} /> ) }) } <br /> <button onClick={addNewTodo}>Add New</button> </div> ); } export default Observer (TodoListFnPage);Copy the code

Step 5️ : Use state in class components

// ./src/containers/todo-list-class/index.tsx import React from 'react'; import { inject, observer } from 'mobx-react'; import { STORE_TODO, TodoStore } from '.. /.. /stores'; import { Todo } from '.. /.. /components'; @inject(STORE_TODO) @observer class TodoListClassPage extends React.Component<{[STORE_TODO]: TodoStore }> { addNewTodo = () => { this.props[STORE_TODO].addNewTodo(); } render() { const { todos, undoneCount, doneCount } = this.props[STORE_TODO]; return ( <div> <div> Done: {doneCount} Undone: {undoneCount} </div> <br /> { todos.map((todo) => { return ( <Todo key={todo.id} todo={todo} onRemove={this.props[STORE_TODO].removeById} switchStatus={this.props[STORE_TODO].toggleStatusById} /> ) }) } <br /> <button onClick={this.addNewTodo}>Add New</button> </div> ); } } export default TodoListClassPage;Copy the code

Step 6️ : Stop work 🎉