多个useEffect和setState导致回调被调用两次

时间:2019-10-07 23:47:55

标签: reactjs rxjs

我正在测试一种模式,该模式是我在网上发现的一种称为减数分裂的模式,可以使用事件流替代Redux。概念很简单,使用scan方法将状态作为更新函数流生成,以根据当前状态评估函数并返回新状态。它在我所有的测试用例中都很好用,但是当我将它与react一起使用时,每个动作都会被调用两次。您可以在CodeSandbox上查看整个应用程序并重现该问题。

import state$, { actions } from "./meiosis";
const App = () => {
  const [todos, setTodos] = useState([]);
  const [newTodo, setNewTodo] = useState({
    title: "",
    status: "PENDING"
  });
  useEffect(() => {
    state$
      .pipe(
        map(state => {
          return state.get("todos")
        }),
        distinctUntilChanged(),
        map(state => state.toJS())
      )
      .subscribe(state => setTodos(state));
  }, []);

  useEffect(() => {
    state$
      .pipe(
        map(state => state.get("todo")),
        distinctUntilChanged(),
        map(state => state.toJS())
      )
      .subscribe(state => setNewTodo(state));
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        {genList(todos)}
        <div className="formGroup">
          <input
            type="text"
            value={newTodo.title}
            onChange={evt => actions.typeNewTodoTitle(evt.target.value)}
          />
          <button
            onClick = {() => {
              actions.addTodo()
            }}
          >
            Add TODO
          </button>
          <button
            onClick={() => {
              actions.undo();
            }}
            >UNDO</button>
        </div>
      </header>
    </div>
  );
};

Meisos

import { List, Record } from "immutable";
import { Subject } from "rxjs";

const model = {
  initial: {
    todo: Record({
      title: "",
      status: "PENDING"
    })(),
    todos: List([Record({ title: "Learn Meiosis", status: "PENDING" })()])
  },
  actions(update) {
    return {
      addTodo: (title, status = "PENDING") => {
        update.next(state => {
            console.log(title);
          if (!title) {
            title = state.get("todo").get("title");
          }
          const todo = Record({ title, status })();
          return state.set("todos", state.get("todos").push(todo));
        });
      },
      typeNewTodoTitle: (title, status = "PENDING") => {
        update.next(state => {
            return state.set("todo", Record({ title, status })())
        });
      },
      resetTodo: () => {
        update.next(state =>
          state.set("todo", Record({ title: "", status: "PENDING" })())
        );
      },
      removeTodo: i => {
        update.next(state => state.set("todos", state.get("todos").remove(i)));
      }
    };
  }
}
const update$ = new BehaviorSubject(state => state) // identity function to produce initial state

export const actions = model.actions(update$);
export default update$;

1 个答案:

答案 0 :(得分:0)

解决我的问题。它源于对RXJS的工作方式的误解。 RxJS github页面上的issue给了我答案。每个订阅都会导致对可观察管道进行重新评估。通过将share运算符添加到管道中,它可以解决此问题。

export default update$.pipe(
    scan(
      (state, updater) => 
        updater(state),
      Record(initial)()
    ),
    share()
  );