【问题标题】:Prevent this.state to be used with setState防止 this.state 与 setState 一起使用
【发布时间】:2018-10-08 09:17:00
【问题描述】:

The reference 状态:

setState() 并不总是立即更新组件。它可能会批量更新或将更新推迟到以后。这使得在调用 setState() 之后立即读取 this.state 是一个潜在的陷阱。相反,使用 componentDidUpdate 或 setState 回调 (setState(updater, callback)),它们都保证在应用更新后触发。如果您需要根据之前的状态设置状态,请阅读下面的 updater 参数。

因此在 React 中将 this.state 值与 setState 一起使用被认为是错误的,因为 setState 是异步的,并且可能导致使用错误的值更新状态(demo):

// destructured
const { state, setState } = this;
setState({ foo: state.foo });

// destructured
const { foo } = this.state;
setState({ foo });

// undestructured
this.setState({ foo: this.state.foo });

虽然这是更新状态的正确方法(demo):

// destructured
this.setState(({ foo }) => ({ foo }));

// undestructured
this.setState(state => ({ foo: state.foo }));

是否有 ESLint 规则或其他方法来防止 this.state 可能被滥用的部分或全部情况?

我认为用静态分析解决这个问题可能很困难,但可以解决。

【问题讨论】:

    标签: javascript reactjs eslint static-analysis


    【解决方案1】:

    eslint-plugin-react 将使用react/no-access-state-in-setstate 规则进行此检查

    此规则应防止在 setState 调用中使用 this.state。当批量调用两个状态调用并因此引用旧状态而不是当前状态时,this.state 的这种用法可能会导致错误。

    【讨论】:

    • 谢谢,这是我正在寻找的规则。可悲的是,它似乎只适用于this.state 未被解构的情况。
    • 静态分析永远不会涵盖所有可能的运行时流程(不仅是解构,而且调用的其他变量或函数也会导致您描述的这种陷阱)但总比没有好。
    【解决方案2】:

    如果你使用:

    // destructured
    const { state, setState } = this;
    setState({ foo: state.foo });
    

    由于state.foo(访问对象的属性),Eslint 仍然会警告您。为避免这种情况,您可以定义如下:

    // destructured
    const { state: { foo }, setState } = this;
    setState({ foo });
    

    但如果你使用:

    // undestructured
    this.setState({ foo: this.state.foo });
    

    然后,ESLINT 会警告你使用解构语法,例如:

    const { foo } = this.state
    this.setState({foo})
    

    注意:由于foo是要更新的变量名,并且名称匹配,我们可以只使用{foo},与{foo: foo}相同。


    不过,我更喜欢使用this.setState() 语法,而不是解构为this。因为,在任何应用程序中,我们都会在必要时使用this。如果我们在看到setState 而不是this.setState 时查看代码,使用const { ... } = this 似乎会令人困惑。以第三位开发者的身份思考。


    从 cmets 中,您想一个接一个地更新状态,那么您应该使用如下回调:

    onClick = () => {
       this.setState({ foo: 'Bar' }, () => {
          this.setState({ foo: this.state.foo + '!' });
       });
    }
    

    现在,您将能够在演示中看到对 Hello Bar! 的更改。

    如果你像这样使用 setState:

    onClick = () => {
       this.setState({ foo: 'Bar' })
       this.setState({ foo: this.state.foo + '!' });
       // obviously, better to use updater syntax though.
    }
    

    那么第一个 setState 将被最后一个覆盖。您将在演示中获得对 Hello Foo! 的更改。

    此外,文档说明相同。 updater 语法只是一种方便的方法,但结果与没有 updater 语法的结果完全相同。最重要的作用只是与它的回调语法有关。使用回调语法,以便您可以在更新后立即访问更新状态。


    了解有关解构语法的更多信息。您可以关注my another post,在那里您可以找到详细信息和一些非常熟悉的链接。

    【讨论】:

      猜你喜欢
      • 2020-03-19
      • 2015-01-17
      • 2019-01-31
      • 2019-01-19
      • 1970-01-01
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多