简体   繁体   English

与 React 功能组件一起使用的 Mobx (TS) 问题

[英]Problem with Mobx (TS) using with React Functional Components

Here is the problem.这是问题所在。

I have simple todo store:我有简单的待办事项商店:

import { makeAutoObservable } from "mobx";
import { Todo } from "./../types";

class Todos {
todoList: Todo[] = [
    { id: 0, description: "Погулять с собакой", completed: false },
    { id: 1, description: "Полить цветы", completed: false },
    { id: 2, description: "Покормить кота", completed: false },
    { id: 3, description: "Помыть посуду", completed: true },
];

// Input: Add Task
taskInput: string = "";

// Filter: query
query: string = "";

// Filter: showOnlyCompletedTasks
showOnlyCompleted: boolean = false;

constructor() {
    makeAutoObservable(this);
}

setShowOnlyCompletedState(value: boolean) {
    this.showOnlyCompleted = value;
}

changeCompletionState(id: number) {
    const task = this.todoList.find((todo) => todo.id === id);
    if (task) task.completed = !task.completed;
}

addTask(text: string) {
    if (text !== "") {
        const newTodo: Todo = {
            id: Number(new Date()),
            description: text,
            completed: false,
        };
        this.todoList.push(newTodo);
    }
}

taskChangeInput(value: string) {
    this.taskInput = value;
}

queryChangeInput(value: string) {
    this.query = value;
}
}

export default new Todos();

In the app I have some tasks, which I can make completed or not-completed (by clicking on it) and also I do have some filters to filter my todo_list.在应用程序中,我有一些任务,我可以完成或未完成(通过单击它),而且我确实有一些过滤器来过滤我的 todo_list。 Here is the code:这是代码:

  • of posts:帖子数:
import { Todo } from "../types";
import { useMemo } from "react";

function useFilterByQuery (list: Todo[], query: string):Todo[] {
    const filteredList = useMemo(()=>{
        if (!query) return list
        return list.filter(todo => todo.description.toLowerCase().includes(query.toLowerCase()))
    }, [list, query])
    return filteredList
}

export function useFilterByAllFilters (list:Todo[], query: string, showOnlyCompleted: boolean):Todo[] {
    const filteredByQuery = useFilterByQuery(list, query)

    const filteredList = useMemo(()=>{
        if(!showOnlyCompleted) return filteredByQuery
        return filteredByQuery.filter(todo => todo.completed)
    }, [filteredByQuery, showOnlyCompleted])

    return filteredList
}

So the description of the problem is so: when I choose show me only-Completed-tasks (setting showOnlyCompleted to true), I get expected list of tasks which are all 'completed'.所以问题的描述是这样的:当我选择只显示完成任务(将 showOnlyCompleted 设置为 true)时,我得到了所有“已完成”的预期任务列表。 But, when I change the state of 'todo' right now, the shown list isn't changing (uncompleted task doesn't filter immediatly), it's changing only after I set showOnlyCompleted to false and back to true .但是,当我现在更改 'todo' 的状态时,显示的列表并没有改变(未完成的任务不会立即过滤),它只有在我将showOnlyCompleted设置为false并返回true后才会改变。

I assume it's not happening, because I don't 'update' the todoList for MobX, which I provide (using function useFilterByAllFilters) by props in My TodoList component.我认为它没有发生,因为我没有“更新” MobX的 todoList,我通过 My TodoList 组件中的道具提供(使用函数 useFilterByAllFilters)。 In my opinion the problem is with the useMemo or with something in Mobx.在我看来,问题出在 useMemo 或 Mobx 中的某些东西上。 Please, help me out.请帮帮我。

Yes, it's because of useMemo .是的,这是因为useMemo useFilterByQuery only has [list, query] deps, but when you change some todo object to be (un)completed you don't really change the whole list , only one object inside of it. useFilterByQuery只有[list, query]部门,但是当您将一些待办事项对象更改为(未)完成时,您并没有真正更改整个list ,只有其中一个对象。 And that is why this useMemo does not rerun/recompute, so you still have the old list.这就是为什么这个useMemo不会重新运行/重新计算,所以你仍然有旧的列表。 Same with useFilterByAllFilters .useFilterByAllFilters相同。

What you are doing is not really idiomatic MobX I would say!你所做的并不是我想说的真正惯用的 MobX! MobX is designed to work with mutable data and React hooks are designed to work with immutable data, so to make it work with hooks you need to do some workarounds, but it would be easier to just rewrite it like that: MobX 设计用于处理可变数据,而 React 钩子设计用于处理不可变数据,因此要使其与钩子一起使用,您需要做一些变通方法,但像这样重写它会更容易:

class Todos {
  // ... your other code

  // Add computed getter property to calculate filtered list
  get listByQuery() {
    if (!this.query) return list
    return this.todoList.filter(todo => todo.description.toLowerCase().includes(this.query.toLowerCase()))
  }

  // Another one for all filters
  get listByAllFilters() {
    if(!this.showOnlyCompleted) return this.listByQuery
    return this.listByQuery.filter(todo => todo.completed)
  }
}

And just use this two computed properties in your React components, that's it!只需在你的 React 组件中使用这两个计算属性,就是这样! No need for hooks.不需要钩子。 And these properties are cached/optimized, ie will only run when some of their observables change.并且这些属性是缓存/优化的,即仅当它们的一些可观察值发生变化时才会运行。

More info about computeds有关计算的更多信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM