4

我正在尝试使用 toString 临时将一个类输出到 DOM。我遇到了一些我不明白覆盖toString()将始终输出初始状态的行为。但是,如果使用外部函数(即 stateToString)甚至JSON.stringify,更新后的状态会按我的预期输出。

下面是我尝试最小化重现这种行为。重申一下,我的预期行为是让他们所有人最初输出:["initial"],他们会这样做。但是,toString()单击按钮时输出不会更新,但其他两个会更新。

这似乎特别奇怪,stateToString并且State.toString似乎本质上是相同的函数,除了一个将状态作为接收器,另一个将状态作为参数。

如果有人能解释为什么会发生这种行为,我将不胜感激。

import React, { useReducer } from 'react';

class State { 
  constructor(xs) { this.xs = xs } 
  toString = () => `[${this.xs}]`
}

const stateToString = state => `[${state.xs}]`;

const reducer = (state, action) => ({
  ...state,
  xs: [...state.xs, action.x]
});

const App = () => {
  const [state, dispatch] = useReducer(reducer, new State(["initial"]));
  return (
    <div>
      <button onClick={() => dispatch({ x: Math.random() })}>click</button><br />
      toString:  {state.toString()}<br />
      print:     {stateToString(state)}<br />
      stringify: {JSON.stringify(state)}
    </div>
  );
};

export default App;
4

1 回答 1

4

toString您放在 State 上的方法绑定到 state 的原始实例

class State { 
  constructor(xs) { this.xs = xs } 
  toString = () => `[${this.xs}]` // Class field arrow function
}

那里的类​​字段意味着无论使用什么调用上下文toString调用,它都会返回this.xs初始状态的。即使 reducer 更新了 state,state 的构造函数也不会再次运行

在后面的调用中App,初始状态被创建,然后通过一些动作来更新它,导致state变量是一个更新的对象,但它仍然有一个toString绑定到初始状态的方法

这是 vanilla JS 中的行为示例:

const obj = {
  val: 'val',
  toString: () => obj.val
};

const copiedObj = { ...obj, val: 'newVal' };
console.log(copiedObj.toString());

如果您分配了 afunction而不是箭头函数,则将使用更新toString状态的调用上下文调用,因为它未绑定到初始状态,因此将使用更新状态的调用上下文调用它并正确检索:_xs

toString = function () {
    return `[${this.xs}]`;
}

作为旁注,您不能使用普通方法,例如

toString() {
    return `[${this.xs}]`;
}

因为在你的减速器中:

const reducer = (state, action) => ({
    ...state,
    xs: [...state.xs, action.x]
});

传播语法只接受可枚举的自己的属性。使用方法语法(如toString() {),该属性放在 State原型上,而不是实际实例上,因此它不会存在于 final 中,而是会调用state内置函数。Object.prototype.toString

于 2020-01-09T05:16:17.360 回答