重新选择选择器使用相同的输入不断重新计算

Reselect selector keeps recomputing with the same input

我不明白为什么重新选择库会使用相同的输入重新计算选择器。我检查了 FAQ,但它没有回答我的问题。

这里有一个 react、redux、reselect 的例子

Codesandbox

import React from "react";
import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider, connect } from "react-redux";
import { createSelector } from "reselect";

const initialState = {
  counter: 1
};

const reducer = (state = initialState, action) => {
  if (action.type === "INCREMENT") {
    return { ...state, counter: state.counter + 1 };
  }
  if (action.type === "DECREMENT") {
    return { ...state, counter: state.counter - 1 };
  }
  return state;
};

const store = createStore(reducer);

class ChildComp extends React.Component {
  handleIncrement = () => {
    this.props.increment();
  };

  handleDecrement = () => {
    this.props.decrement();
  };

  render() {
    const { counter, isEven } = this.props;
    return (
      <div>
        <div>counter: {counter}</div>
        <div>is even: {JSON.stringify(isEven)}</div>
        <button onClick={this.handleIncrement}>increment</button>
        <button onClick={this.handleDecrement}>decrement</button>
      </div>
    );
  }
}

const getCounter = state => state.counter;
const isEvenSelector = createSelector(
  getCounter,
  counter => {
    console.log("calculate is even for", counter);
    return counter % 2 === 0;
  }
);

const mapStateToProps = state => {
  return {
    counter: state.counter,
    isEven: isEvenSelector(state)
  };
};

const mapDispatchToProps = dispatch => {
  return {
    increment: () => dispatch({ type: "INCREMENT" }),
    decrement: () => dispatch({ type: "DECREMENT" })
  };
};

const Child = connect(
  mapStateToProps,
  mapDispatchToProps
)(ChildComp);

class App extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Child />
      </Provider>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

预期行为:

initial (counter 1) // 计算为偶数 1
increment (counter 2) // 计算对于 2
是偶数 decrement (counter 1) // 不应该console.log,因为结果已经计算

实际行为:

initial (counter 1) // 计算为偶数 1
increment (counter 2) // 计算对于 2
是偶数 decrement (counter 1) // 计算对于 1

是偶数

仅缓存前一组输入的结果。

The docs say:

Selectors created with createSelector have a cache size of 1. This means they always recalculate when the value of an input-selector changes, as a selector only stores the preceding value of each input-selector.

所以,如果计数器的值没有改变,那么您将看不到控制台日志。但是因为和上次选择器函数执行的结果不一样,所以需要重新计算。