【问题标题】:React: trigger onChange if input value is changing by state?反应:如果输入值按状态更改,则触发 onChange?
【发布时间】:2017-07-21 21:05:47
【问题描述】:

编辑:我不想仅在单击按钮时才调用 handleChange。它与handleClick 无关。我在@shubhakhatri 答案的评论中举了一个例子。

我想根据状态改变输入值,值在改变但不会触发handleChange()方法。如何触发handleChange() 方法?

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
    value: 'random text'
    }
  }
  handleChange (e) {
    console.log('handle change called')
  }
  handleClick () {
    this.setState({value: 'another random text'})
  }
  render () {
    return (
      <div>
        <input value={this.state.value} onChange={this.handleChange}/>
        <button onClick={this.handleClick.bind(this)}>Change Input</button>
      </div>
    )
  }
}

ReactDOM.render(<App />,  document.getElementById('app'))

这里是codepen链接:http://codepen.io/madhurgarg71/pen/qrbLjp

【问题讨论】:

  • 据我了解,您只想在单击按钮后更改输入。我说的对吗??
  • @ShubhamKhatri 抱歉,我错了,我不想在单击按钮后才更改。这只是handleClick 只是设置状态的示例。它与handleClick 无关。对不起,我解释错了。
  • 你发现了吗?据我所知,上述脚本没有理由不工作,至少在最新的 React 版本中是这样。我刚刚在提供的 Codepen sn-p 中试了一下,效果很好。
  • 您在 codepen 上的内容与此处显示的内容不同。这工作正常,绝对没有理由不工作。您可能想在控制台上检查“调用的处理更改”
  • 应该使用功能组件

标签: javascript reactjs


【解决方案1】:

您需要手动触发onChange 事件。在文本输入时,onChange 会监听 input 事件。

所以在你handleClick 函数中你需要触发像

这样的事件
handleClick () {
    this.setState({value: 'another random text'})
    var event = new Event('input', { bubbles: true });
    this.myinput.dispatchEvent(event);
  }

完整代码

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
    value: 'random text'
    }
  }
  handleChange (e) {
    console.log('handle change called')
  }
  handleClick () {
    this.setState({value: 'another random text'})
    var event = new Event('input', { bubbles: true });
    this.myinput.dispatchEvent(event);
  }
  render () {
    return (
      <div>
        <input readOnly value={this.state.value} onChange={(e) => {this.handleChange(e)}} ref={(input)=> this.myinput = input}/>
        <button onClick={this.handleClick.bind(this)}>Change Input</button>
      </div>
    )
  }
}

ReactDOM.render(<App />,  document.getElementById('app'))

Codepen

编辑: 正如 @Samuel 在 cmets 中所建议的那样,如果您在 handleChange 中喜欢 don't need to the event object,则更简单的方法是从 handleClick 调用 handleChange

handleClick () {
    this.setState({value: 'another random text'})
    this.handleChange();
  }

我希望这是你需要的,它可以帮助你。

【讨论】:

  • 在元素上使用 ref 几乎总是错误的答案,除非你需要做一些其他方式无法做到的事情。在这里,一个更简单的解决方案是简单地从 handleClick 调用 handleChange,正如这个答案中所建议的那样:stackoverflow.com/a/42550931/2608603
  • @SamuelScheiderich 如果您不希望handleChange 中的onChange 事件对象,当然是一种更简单的方法。我没有将它包含在原始答案中,因为 handleChange 包含事件作为参数
  • @ShubhamKhatri 抱歉,我要撤回我的支持。我刚刚意识到你的回答并没有回答我的问题。您正在做的是从 handleClick 显式调用 handleChange 方法。这与handleClick 无关。 React state 不知道 handleClick 方法,对吧?我会给你一个更实际的例子,我们可能需要这个。假设您有一个登录表单并且您保存了密码。当你再次进入登录页面时,浏览器会自动填写表单输入。现在你想为一些 UI 动画调用你的 handleChange 方法。
  • 对于 2019 年根本不起作用。由于某种原因,表单现在对手动 dispatchEvent 没有反应
  • @ShubhamKhatri,Shubham 美好的一天,很好的答案,我想问你一个问题,我们是否应该始终对输入字段使用 onChange 处理程序。我只是在创建计算器并决定使用例如 而不使用 onChange。我只是改变状态,然后输入的值也会改变。这是正确的方法吗?还是应该在使用输入字段时始终使用 onChange?:)
【解决方案2】:

2020 年和 2021 年的解决方案:

我尝试了其他解决方案,但没有任何效果。这是因为 React.js 中的输入逻辑已经改变。具体可以看这个链接:https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04

简而言之,当我们通过改变 state 来改变 input 的值,然后派发一个 change 事件,那么 React 会同时注册 setState 和 event 并认为它是一个重复的事件并吞下它。

解决方案是在输入时调用原生值设置器(参见下面代码中的 setNativeValue 函数)

示例代码

import React, { Component } from 'react'
export class CustomInput extends Component {

    inputElement = null;
    
    // THIS FUNCTION CALLS NATIVE VALUE SETTER
    setNativeValue(element, value) {
        const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
        const prototype = Object.getPrototypeOf(element);
        const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;

        if (valueSetter && valueSetter !== prototypeValueSetter) {
            prototypeValueSetter.call(element, value);
        } else {
            valueSetter.call(element, value);
        }
    }


    constructor(props) {
        super(props);

        this.state = {
            inputValue: this.props.value,
        };
    }

    addToInput = (valueToAdd) => {
        this.setNativeValue(this.inputElement, +this.state.inputValue + +valueToAdd);
        this.inputElement.dispatchEvent(new Event('input', { bubbles: true }));
    };

    handleChange = e => {
        console.log(e);
        this.setState({ inputValue: e.target.value });
        this.props.onChange(e);
    };

    render() {
        return (
            <div>
                <button type="button" onClick={() => this.addToInput(-1)}>-</button>
                <input
                    readOnly
                    ref={input => { this.inputElement = input }}
                    name={this.props.name}
                    value={this.state.inputValue}
                    onChange={this.handleChange}></input>
                <button type="button" onClick={() => this.addToInput(+1)}>+</button>
            </div>
        )
    }
}

export default CustomInput

结果

【讨论】:

  • 这正是我要找的,谢谢队友 :)
  • prototypeValueSetter.callvalueSetter.call 交换了吗?在上面的示例中,valueSetter 只有在 a) 未定义或 b) 严格等于 prototypeValueSetter 时才会被调用。
  • 我测试了他们切换的代码 - 不起作用。因此,我认为没有理由不专门使用prototypeValueSetter 并删除所有提及valueSetter
  • 为什么setNativeValue() 中的if() 条件是必需的?换句话说,我们不能只做Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set.call(element, value);吗? (请原谅单行;希望在 cmets 中更容易阅读单行)
【解决方案3】:

我认为你应该这样改变:

<input value={this.state.value} onChange={(e) => {this.handleChange(e)}}/>

原则上与您在按钮上所做的onClick={this.handleClick.bind(this)} 相同。

所以如果你想在点击按钮时调用handleChange(),那么:

<button onClick={this.handleChange.bind(this)}>Change Input</button>

handleClick () {
  this.setState({value: 'another random text'});
  this.handleChange();
}

【讨论】:

  • 我想在点击按钮时触发handleChange。它通过在输入中手动输入来工作。假设输入是只读的。
【解决方案4】:

其他答案谈到了渲染中的直接绑定,因此我想补充几点。

不建议直接在render中或组件中除构造函数之外的任何地方绑定函数。因为对于每个函数绑定,都会在 webpack bundle js 文件中创建一个新的函数/对象,因此 bundle 的大小会增加。您的组件将出于多种原因重新渲染,例如当您执行 setState 时、收到新道具时、当您执行 this.forceUpdate() 等时。因此,如果您直接在渲染中绑定您的函数,它将始终创建一个新函数。相反,总是在构造函数中进行函数绑定,并在需要的地方调用引用。这样,它只创建一次新函数,因为每个组件只调用一次构造函数。

你应该怎么做,如下所示

constructor(props){
  super(props);
  this.state = {
    value: 'random text'
  }
  this.handleChange = this.handleChange.bind(this);
}

handleChange (e) {
  console.log('handle change called');
  this.setState({value: e.target.value});
}

<input value={this.state.value} onChange={this.handleChange}/>

您也可以使用箭头函数,但在某些情况下,每次组件重新渲染时,箭头函数也会创建新函数。你应该知道什么时候使用箭头函数,什么时候不应该使用。有关何时使用箭头函数的详细说明,请查看接受的答案here

【讨论】:

  • 这个答案完全忽略了实际问题。虽然您的答案中提供的信息一般而言很有价值,但我认为不应该存在不相关的答案,因为它只会让人们试图找到真正的有用 (或密切)回答主题
【解决方案5】:

您必须执行以下 4 个步骤:

  1. 创建事件

    var event = new Event("change",{
        detail: {
            oldValue:yourValueVariable,
            newValue:!yourValueVariable
        },
        bubbles: true,
        cancelable: true
    });
    event.simulated = true;
    let tracker = this.yourComponentDomRef._valueTracker;
    if (tracker) {
        tracker.setValue(!yourValueVariable);
    }
    
  2. 将值绑定到组件 dom

    this.yourComponentDomRef.value = !yourValueVariable;
    
  3. 绑定元素onchange以响应onChange函数

     this.yourComponentDomRef.onchange = (e)=>this.props.onChange(e);
    
  4. 调度事件

    this.yourComponentDomRef.dispatchEvent(event);
    

在上面的代码中yourComponentDomRef 指的是你的 React 组件的主 dom,例如 &lt;div className="component-root-dom" ref={(dom)=&gt;{this.yourComponentDomRef= dom}}&gt;

【讨论】:

  • 这是一堆你不想解释的不正确的伏都教代码集合。根据我的经验,确实只有_valueTrackerevent.simulated = true 是必要的,但其余的都没有意义。 为什么你会这样做value = !yourValueVariable?完全没有意义。不妨添加5. jump on your head and roll twice on your back 作为“必须做”所谓的步骤的一部分。
【解决方案6】:

使用 React Native 和 Hooks 的方法:

您可以将 TextInput 包装到一个新的输入中,以观察值是否发生变化,如果发生变化则触发 onChange 函数。

import React, { useState, useEffect } from 'react';
import { View, TextInput as RNTextInput, Button } from 'react-native';

// New TextInput that triggers onChange when value changes.
// You can add more TextInput methods as props to it.
const TextInput = ({ onChange, value, placeholder }) => {

  // When value changes, you can do whatever you want or just to trigger the onChange function
  useEffect(() => {
    onChange(value);
  }, [value]);

  return (
    <RNTextInput
      onChange={onChange}
      value={value}
      placeholder={placeholder}
    />
  );
};

const Main = () => {

  const [myValue, setMyValue] = useState('');

  const handleChange = (value) => {
    setMyValue(value);
    console.log("Handling value");
  };

  const randomLetters = [...Array(15)].map(() => Math.random().toString(36)[2]).join('');

  return (
    <View>
      <TextInput
        placeholder="Write something here"
        onChange={handleChange}
        value={myValue}
      />
      <Button
        title='Change value with state'
        onPress={() => setMyValue(randomLetters)}
      />
    </View>
  );
};

export default Main;

【讨论】:

    【解决方案7】:

    在功能组件中你可以这样做,假设我们有一个input[type=number]

    const MyInputComponent() => {
        const [numberValue, setNumberValue] = useState(0);
        const numberInput = useRef(null);
    
        /**
        * Dispatch Event on Real DOM on Change
        */
        useEffect(() => {
            numberInput.current.dispatchEvent(
                new Event("change", {
                    detail: {
                        newValue: numberValue,
                    },
                    bubbles: true,
                    cancelable: true,
                })
            );
        }, [numberValue]);
    
        return (
            <>
                <input    
                    type="number"            
                    value={numberValue}             
                    ref={numberInput}
                    inputMode="numeric"
                    onChange={(e) => setNumberValue(e.target.value)}
                />
            </>
        )
    }
    

    【讨论】:

      【解决方案8】:

      我知道你的意思,你想通过点击按钮触发handleChange。

      但是修改状态值不会触发onChange事件,因为onChange事件是一个表单元素事件。

      【讨论】:

      • 但是当您保存登录凭据和浏览器的自动填充设置输入值时,它是如何被触发的。
      • 其实我觉得没有必要触发那个功能,或许你应该告诉我你想做什么动作。
      【解决方案9】:

      如果状态对象有像this.state.class.fee 这样的子对象,试试这个代码。我们可以使用以下代码传递值:

      this.setState({ class: Object.assign({}, this.state.class, { [element]: value }) }
      

      【讨论】:

      • 你好 Atchutha!请使用正确的代码格式,以便内容易于阅读。
      【解决方案10】:

      我有类似的需求,最终使用了 componentDidMount(),它在组件类构造函数之后很长时间被调用(您可以在其中从 props 初始化状态 - 例如使用 redux )

      在 componentDidMount 中,您可以为某些 UI 动画调用您的 handleChange 方法或执行所需的任何类型的组件属性更新。

      例如,我在以编程方式更新输入复选框类型时遇到问题,这就是我最终使用此代码的原因,因为 onChange 处理程序在组件加载时没有触发:

         componentDidMount() {
      
          // Update checked 
          const checkbox = document.querySelector('[type="checkbox"]');
      
          if (checkbox) 
            checkbox.checked = this.state.isChecked;
        }
      

      状态首先在组件类构造函数中更新,然后用于更新某些输入组件行为

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-02
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        • 2020-12-28
        相关资源
        最近更新 更多