在React

时间:2018-02-22 16:05:47

标签: javascript performance reactjs flux

React新手的常见问题是双向数据绑定不是内置功能的原因,通常的响应包括单向数据流的解释以及双向数据绑定并不总是令人满意的想法出于性能原因。这是我想要更详细了解的第二点。

我目前正在为apollo-link-state(来自Apollo的新客户端状态管理工具)的表单库工作。除了使用apollo-link-state而不是redux作为状态管理器之外,这个概念与redux-form非常相似。 (请注意,表单状态与域实体的状态分开存储,尽管可以选择使用实体来填充表单的初始状态。)

当用户在表单上进行更改时,库会立即通过onChange处理程序更新商店。我正考虑允许个别字段选择退出该行为,以防程序员关注性能,但后来我开始怀疑这何时会成为真正的性能问题。无论如何,浏览器都会触发oninput事件,因此我能想到的唯一性能考虑因素是存储是否随用户类型而更新。当然,执行突变而不仅仅是调用setState()会产生额外的开销,但这实际上只相当于一些额外的函数调用。让我们假设我没有使用apollo而只是调用一个直接更新某个全局存储的函数 - 那么性能考虑是什么呢?

我的想法是,如果表单支持在用户键入一个字段时立即更新表单状态,那么对于所有字段也可以这样做。用户一次只能键入一个字段,我看不到使某些字段有时更快(可能忽略不计)的页面的好处,有时候会慢一些。此外,我的库允许消费者使用他们想要的任何输入组件,因此如果程序员只想要更少的状态更新,他们可以编写一个组件来消除React的onChange事件或使用浏览器自己的change或而是blur事件。

我在这里遗漏了什么吗?在用户提交表单之前,我的库的用户是否还想忽略特定字段的更改?或者更有用的选择是忽略对整个表单的更改(直到提交)?

这是我当前方法背后的基本概念的基本(大大简化)说明:

// defined in a globally-accessible module
const formState = {
    // This somehow causes any dependent form components to re-render
    // when state changes
    update(formName, updatedState) {
        ...
    }
}
export default formState

...
// UserForm.js:

export default class UserForm extends PureComponent {
    componentDidMount() {
        formState.userForm = {
            firstName: '',
            lastName: '',
        }
    }

    handleChange(e) {
        const { target } = e
        formState.update('userForm', { [target.name]: target.value })
    }

    //...

    render() {
        const { userForm } = formState
        return (
            <form onSubmit={this.handleSubmit}>
                <label for="name">Name</label>
                <input id="name" type="text" onChange={this.handleChange} value={userForm.name} />

                <label for="email">Email</label>
                <input id="email" type="email" onChange={this.handleChange} value={userForm.email} />
            </form>
        )
    }
}

最后,为了完整起见,我应该提一下,还有一些API设计考虑因素。如果我提供选择退出自动双向绑定的选项,则单个输入组件可以具有稍微简单的设计。如果有人有兴趣,我可以发布详细信息。

1 个答案:

答案 0 :(得分:21)

2路数据绑定影响

从问题的第一部分开始,有两个主要原因可以解决双向数据绑定:

  1. React应用中数据更改的单一事实来源,因此错误发生的机会更少,调试也更轻松
  2. 绩效优势
  3. 在React中,我们可以通过lifting the state up将不同子组件之间的状态共享到公共父组件。更新共享状态时,所有子组件都可以自行更新。 Here是与表格相关的文档中的一个很好的例子。

    谈到性能优势,在其他一些环境中使用双向数据绑定(比如AngularJS)可以通过观察者观察不同的元素来实现。对于少量元素,这听起来更容易(并且代码比React的单向数据流更少),但随着UI组件/元素的数量增加,观察者的数量也增加。在这种情况下,单个更改会导致许多观察者启动以保持同步。这使得性能有点迟缓。在React的情况下,由于数据流只有一种方式,因此更容易确定哪些组件需要更新。

    处理状态更新

    进入问题的第二部分,您的状态库会向表单组件提供数据,从而导致任何相关组件在状态更改时更新。以下是我的想法:

      

    我正在考虑允许个别字段选择退出   程序员关心性能时的行为,但是   然后我开始怀疑这将是一次真正的表演   问题。

    商店更新本身会很快发生。 JavaScript运行速度非常快,它是DOM更新,经常会导致瓶颈。因此,除非在同一页面上有数百个依赖表单元素并且所有这些元素都已更新,否则您将会很好。

      

    让我们假设我没有使用阿波罗而只是打电话给阿波罗   直接更新一些全局商店的功能 - 将是什么   性能考虑呢?

    我认为它不会有显着差异。

      

    我的想法是,如果表单支持立即更新   当用户在一个字段中键入表单状态时,它也可以这样做   对于所有领域。用户一次只能输入一个字段,并且   我没有看到使页面有时更快的好处(可能   (可忽略不计)有些领域,有时甚至比其他领域慢。

    同意这一点。

      

    我的库允许消费者使用他们的任何输入组件   想要,所以如果程序员只想要更少的状态更新,他们就可以   只需编写一个可以去除React的onChange事件或使用的组件   相反,浏览器自己的更改或模糊事件。

    我认为大多数用例都可以通过简单的input来解决。同样,我不会在这里看到更少的状态更新,从而获得性能优势。如果我在输入上运行API调用(并希望在用户停止输入之前等待),则去抖可能很有用。

      

    还有其他原因导致我的图书馆用户想要这样做   在用户提交表单之前忽略特定字段的更改?   或者更有用的选择可能是忽略对的更改   整个表格(直到提交)?

    我没有看到忽略特定领域的变化或等到提交之前的好处。另一方面,在使用表单时,我遇到的常见用例是数据验证。例如,

    • 在用户创建密码时向用户提供反馈
    • 检查电子邮件是否有效
    • 执行API调用以查看用户名是否有效等等。

    这些情况需要在用户输入时更新状态。

    TL;博士

    在用户输入时更新状态应该没问题。如果您仍然关注性能,我建议profile your components隔离瓶颈,如果有的话:)