我试图理解一些我无法完全解释的"神奇"行为的根本原因,而这在阅读ReactJS源代码时并不明显.

当同步调用setState方法以响应输入上的onChange事件时,一切正常.输入的"新"值已经存在,因此DOM实际上没有更新.这是非常可取的,因为这意味着光标不会跳到输入框的末尾.

然而,当运行一个 struct 完全相同但调用setState asynchronously的组件时,输入的"新"值似乎不存在,导致ReactJS实际接触DOM,从而导致光标跳到输入的末尾.

显然,在异步情况下,有什么东西正在干预将输入"重置"到之前的value,而在同步情况下则没有这样做.这是什么机修工?

Synchronous Example

var synchronouslyUpdatingComponent =
    React.createFactory(React.createClass({
      getInitialState: function () {
        return {value: "Hello"};
      },

      changeHandler: function (e) {
        this.setState({value: e.target.value});
      },

      render: function () {
        var valueToSet = this.state.value;

        console.log("Rendering...");
        console.log("Setting value:" + valueToSet);
        if(this.isMounted()) {
            console.log("Current value:" + this.getDOMNode().value);
        }

        return React.DOM.input({value: valueToSet,
                                onChange: this.changeHandler});
    }
}));

注意,代码将登录render方法,打印出实际DOM node 的当前value.

在"Hello"的两个L之间键入"X"时,我们会看到以下控制台输出,光标停留在预期的位置:

Rendering...
Setting value:HelXlo
Current value:HelXlo

Asynchronous Example

var asynchronouslyUpdatingComponent =
  React.createFactory(React.createClass({
    getInitialState: function () {
      return {value: "Hello"};
    },

    changeHandler: function (e) {
      var component = this;
      var value = e.target.value;
      window.setTimeout(function() {
        component.setState({value: value});
      });
    },

    render: function () {
      var valueToSet = this.state.value;

      console.log("Rendering...");
      console.log("Setting value:" + valueToSet);
      if(this.isMounted()) {
          console.log("Current value:" + this.getDOMNode().value);
      }

      return React.DOM.input({value: valueToSet,
                              onChange: this.changeHandler});
    }
}));

这与上面完全相同,只是对setState的调用是在setTimeout回调中.

在这种情况下,在两个Ls之间键入X会产生以下控制台输出,光标会跳到输入的末尾:

Rendering...
Setting value:HelXlo
Current value:Hello

Why is this?

我理解React的Controlled Component的概念,因此忽略用户对value的更改是有道理的.但看起来value实际上已经更改,然后显式重置.

显然,同步调用setState可以确保重置生效,而在任何其他时间调用setState都会发生重置,从而强制重新渲染.

事实上是这样吗?

JS Bin Example

http://jsbin.com/sogunutoyi/1/

推荐答案

下面是发生的事情.

同步的

  • 按X键
  • 输入值为"HelXlo"
  • 你打setState({value: 'HelXlo'})
  • 虚拟dom表示输入值应为"HelXlo"
  • 输入值为"HelXlo"
    • 没有采取行动

异步的

  • 按X键
  • 输入值为"HelXlo"
  • 你什么都不做
  • 虚拟DOM表示输入值应该是"Hello"

过后

  • setState({value: 'HelXlo'})
  • 虚拟DOM表示输入值应为"HelXlo"

魔术

是的,这里有一点魔力.React调用在事件处理程序之后同步呈现.这对于避免闪烁是必要的.

Reactjs相关问答推荐

为多租户SSO登录配置Azure AD应用程序

React中的useEffect钩子

如何在与AntD的react 中限制文件上传和显示消息?

在react上隐藏源映射

有没有办法在 React Router v6 中的路由匹配上运行一些逻辑/功能?

无法通过 fetch 获取数据到上下文中

我可以同时使用 Gatsby 和 NEXT.JS 吗?

下一个js如何从另一个组件更新组件?

如何到达类组件中的状态?

在react 中从外部 api 渲染列表

Next.js i18n 路由不适用于动态路由

使用带有搜索字符串的全局过滤器时如何在 React-Table 中进行精确匹配?

单元测试 useLoaderData() react-router v6 加载器函数

如何在对话素材ui中设置边框半径?

如何从 graphql/apollo 中的缓存中清除多个查询

react-query、react-hook-form 和表单验证

在react 钩子中将数组添加到数组状态给出一个数字

哪个 PrivateRouter 实现更好:高阶组件还是替换?

npm init vite@latest 和 npm init vite 有什么区别?

ClearInterval 在 React 中的 useRef 没有按预期工作