js如何在外部改变React受控组件的状态量?

核心代码:

let input = document.getElementsByClassName("input")[0];
input.value = 'new value';
let event = new Event('input', { bubbles: true });
// hack React15
event.simulated = true;
// hack React16 内部定义了descriptor拦截value,此处重置状态
let tracker = input._valueTracker;
if (tracker) {
  tracker.setValue(input);
}
input.dispatchEvent(event);

chrome插件,可以自动触发预设的一系列元素事件。

比如通过点击开始百度按钮,该插件就可以直接给百度搜索输入框填入"关键词"然后自动回车搜索。需求提炼出来其实就是自动化触发前端页面dom元素的一系列事件,主要分为两类:

  1. 键盘事件,比如input元素输入赋值,keyCode13回车事件
  2. 鼠标事件,比如click事件,mouseover事件,scroll滚动事件

1.首先从最简单的触发,只考虑原生html/js开发的网站:
input元素赋值很简单

$eventTarget.value="关键词"

触发回车事件也很简单

$eventTarget.click()
// 或者
$eventTarget.dispatchEvent(new MouseEvent('click'));

触发hover事件,类似的,都可以通过Event创建事件来dispatch触发

$eventTarget.dispatchEvent(new Event("mouseover"))

触发页面滚动事件

document.documentElement.scrollTop = 100

2.然而对于不同mvvm框架(react/vue)开发的页面如何触发的实现都或多或少有点不同。
比如对于react组件库,antd官方一个简单的form表单

import React, { Component } from 'react'
 
class Register extends Component {
  // 在构造函数当中设置状态
  constructor(props){
    super(props)
    this.state ={
      name : '',
      email:'',
      password:'',
      password2:'',
      errors:{},//用户不合法信息提示
    }
  }
  onChange(e){
    console.log(e)
    console.log(e.target.name)//(e.target.name代表你当前输入Input框对应的Name,如email,password
    // e.target.value 代表当前输入的值
    this.setState({
      [e.target.name] : e.target.value
    })
  }
 //提交对应的内容
  onSubmit(e){
    e.preventDefault()
    const newUser = {
      name : this.state.name,
      email:this.state.email,
      password:this.state.password,
      password2:this.state.password2,
    }
    console.log(newUser)
  } 
  render() {
    return (
      <div className="register">
        {/* {user ? user.name : null} */}
        <div className="container">
          <div className="row">
            <div className="col-md-8 m-auto">
              <h1 className="display-4 text-center">注册</h1>
              <p className="lead text-center">创建新的账户</p>
              <form onSubmit={this.onSubmit.bind(this)}>
                <div className="form-group">
                  <input
                    id="input"
                    type="text"
                    className="form-control form-control-lg input"
                    placeholder="用户名"
                    name="name"
                    value = {this.state.name}
                    onChange ={this.onChange.bind(this) }
                  />
                </div>
                <div className="form-group">
                  <input
                    type="email"
                    className='form-control form-control-lg'
                    placeholder="邮箱地址"
                    name="email"
                    value = {this.state.email}
                    onChange ={this.onChange.bind(this) }
                    />
 
                  <small className="form-text text-muted">测试</small>
                </div>
                <div className="form-group">
                  <input
                    type="password"
                    className='form-control form-control-lg'
                    placeholder="密码"
                    name="password"
                    value = {this.state.password}
                    onChange ={this.onChange.bind(this) }
                  />
                </div>
                <div className="form-group">
                  <input type="password"  
                    className='form-control form-control-lg'  
                    placeholder="确认密码"  
                    name="password2" 
                    value = {this.state.password2}
                    onChange ={this.onChange.bind(this) }
                     />
                </div>
                <input type="submit" className="btn btn-info btn-block mt-4" />
              </form>
            </div>
          </div>
        </div>
      </div >
    )
  }
}
export default Register;

受控组件了,直接修改dom,value肯定不生效的啊?

是的

可以尝试使用dispatchEvent去触发dom的赋值行为,这是dispatch一个冒泡阶段的事件

对于15和16版本有不同的hack方法,这两种hack方法分别对应react-dom内部input元素渲染的两种机制分别是怎样的,这个simulated和valueTracker干嘛的?

带着这两个疑问开始翻看源码探究:
一个input标签的渲染:

ReactDOM.render(
<Input type="text"  
  value={state} 
  onClick={e => setState(e.target.value)}
/>,
document.getElementById("root"))
上一篇:Map-LinkedHashMap源码笔记


下一篇:javascript – 非活动键盘x mili秒后自动提交表单