2017-06-15 157 views
0

粘贴到我的电子邮件后,我无法弄清为什么在文本框中复制它。在React Component的文本框中复制文本的粘贴

screencast of the problem

看来,当我粘贴,它正确地触发handleEmailPaste,但我也注意到,handleEmailPaste也得到由过去那么不知道为什么触发。我猜粘贴是一种变化,所以在文本中粘贴可能会触发这两种功能。 如果我注释掉handleEmailInput中的代码并粘贴一个值,它不会重复它。

我想我不知道处理这个问题的正确方法。对我来说似乎我确实需要两个独立的处理程序。请注意,我使用的是引导和控制它,我已经有了的onChange和onPaste集:

<FormControl 
       bsSize="small" 
       className="ft-username" 
       componentClass="input" 
       onPaste={this.props.handleEmailPaste} 
       onChange={this.props.handleEmailInput} 
       placeholder="Enter email" 
       style={{ width: 300}} 
       type="email" 
       value={this.props.email} 
      /> 

LoginContainer

import { connect } from 'react-redux' 
import React, { Component } from 'react' 
const zxcvbn = require('zxcvbn'), 
    _ = require('lodash') 

import * as AsyncActions from '../actions/Auth/AuthAsyncActions' 
import Login from '../components/Login/Login' 



class LoginContainer extends Component { 
    constructor(props) { 
    super(props) 
    this.state = { 
     email: '', 
     password: '', 
     errorMessage: '', 
     emailValidationState: null, 
     formIsValid: false, 
     formValidationState: null, 
     passwordValidationState: null, 
     passwordIsValid: null 
    } 

    this.handleEmailPaste = this.handleEmailPaste.bind(this) 
    this.handleEmailInput = this.handleEmailInput.bind(this) 
    this.handlePasswordInput = this.handlePasswordInput.bind(this) 
    this.handleLoginPressed = this.handleLoginPressed.bind(this) 
    this.resetFields = this.resetFields.bind(this) 
    this.validateForm = this.validateForm.bind(this) 
    this.validateEmail = this.validateEmail.bind(this) 
    this.validatePassword = this.validatePassword.bind(this) 
    } 

    handlePasswordInput(e) { 
    const password = e.target.value 
    this.setState({ password: password}) 
    this.validatePassword() 
    } 

    handleEmailPaste(e){ 
    console.log(`handleEmailPaste: ${e.clipboardData.getData('Text')}`) 
    const value = e.clipboardData.getData('Text') 
    this.setState({ email: value }) 
    this.validateEmail(value) 
    } 

    handleEmailInput(e) { 
    this.setState({ email: e.target.value }) 
    this.validateEmail() 
    } 

    async handleLoginPressed(e) { 
    e.preventDefault() 
    this.validateForm() 

    await this.props.authenticate(this.state.email, this.state.password) 
    if(this.props.isAuthenticated) { 
     this.props.history.push('/dashboard') 
     return 
    } 

    if(!this.props.isAuthenticated){ 
     this.setState({ 
     formValidationState: 'error', 
     errorMessage: this.state.formIsValid && 
     'Your password and/or email is not associated with an active user' 
     }) 

     if(this.state.email && this.state.password){this.resetFields()} 
    } 
    } 

    validateForm(){ 
    this.validateEmail() 
    this.validatePassword() 
    this.setState({ 
     formIsValid: (this.state.emailValidationState === 'success' 
     && this.state.passwordValidationState === 'success')}) 
    } 

    validatePassword(){ 
    const password = zxcvbn(this.state.password) 
    if(password.score >=0){ 
     this.setState({ 
     passwordValidationState: 'error', 
     passwordHelpText: password.feedback.suggestions}) 
     return 
    } 

    this.setState({ 
     passwordValidationState: 'success', 
     passwordHelpText: null }) 
    } 

    validateEmail(value){ 
    if((!_.isEmpty(value)) || !_.isEmpty(this.state.email)) { 
     this.setState({ 
     emailValidationState: 'success', 
     emailError: '' 
     }) 
     return 
    } 

    this.setState({ 
     emailValidationState: 'error', 
     emailError: 'please enter an email address' 
    }) 
    } 

    resetFields(){ 
    this.setState({ 
     email: '', 
     emailError: '', 
     emailValidationState: null, 
     password: '', 
     passwordHelpText: '', 
     passwordValidationState: null }) 
    } 

    render(){ 
    return(
     <div> 
     <Login 
      email={this.state.email} 
      emailError={this.state.emailError} 
      emailValidationState={this.state.emailValidationState} 
      errorMessage={this.state.errorMessage} 
      formValidationState={this.state.formValidationState} 
      handleEmailInput={this.handleEmailInput} 
      handleEmailPaste={this.handleEmailPaste} 
      handlePasswordInput={this.handlePasswordInput} 
      login={this.handleLoginPressed} 
      password={this.state.password} 
      passwordHelpText={this.state.passwordHelpText} 
      passwordValidationState={this.state.passwordValidationState} 
     /> 
     </div> 
    ) 
    } 
} 

const mapStateToProps = state => ({ 
    isAuthenticating: state.auth.isAuthenticating, 
    isAuthenticated: state.auth.isAuthenticated, 
    token: state.auth.token 
}) 

export const mapDispatchToProps = { 
    authenticate: AsyncActions.authenticate 
} 

export { Login } 
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer) 

登录

import React, {Component} from 'react' 

import LoginForm from './LoginForm' 

export default class Login extends Component { 
    render(){ 
     return (
     <div> 
      <LoginForm 
      email={this.props.email} 
      emailError={this.props.emailError} 
      emailValidationState={this.props.emailValidationState} 
      errorMessage={this.props.errorMessage} 
      formValidationState={this.props.formValidationState} 
      handleEmailInput={this.props.handleEmailInput} 
      handleEmailPaste={this.props.handleEmailPaste} 
      handlePasswordInput={this.props.handlePasswordInput} 
      login={this.props.login} 
      password={this.props.password} 
      passwordHelpText={this.props.passwordHelpText} 
      passwordValidationState={this.props.passwordValidationState} 
      /> 
     </div> 
    ) 
    } 
} 

LoginForm

import React, { Component } from 'react' 
import { 
    Button, 
    ControlLabel, 
    HelpBlock, 
    FormControl, 
    FormGroup, 
    PageHeader } from 'react-bootstrap' 

export default class LoginForm extends Component { 
    render(){ 
    return (
     <div className='ft-login-form'> 
     <PageHeader className='ft-header'><small>Login</small></PageHeader> 
     <form onSubmit={this.props.login}> 
      <FormGroup validationState={this.props.formValidationState}> 
      <ControlLabel className="ft-form-error-message">{this.props.errorMessage}</ControlLabel> 
      </FormGroup> 
      <FormGroup controlId="formBasicText" validationState={this.props.emailValidationState}> 
      <ControlLabel>Email</ControlLabel> 
      <FormControl 
       bsSize="small" 
       className="ft-username" 
       componentClass="input" 
       onPaste={this.props.handleEmailPaste} 
       onChange={this.props.handleEmailInput} 
       placeholder="Enter email" 
       style={{ width: 300}} 
       type="email" 
       value={this.props.email} 
      /> 
      <HelpBlock className="ft-email-error">{this.props.emailError}</HelpBlock> 
      </FormGroup> 
      <FormGroup validationState={this.props.passwordValidationState}> 
      <ControlLabel>Password</ControlLabel> 
      <FormControl 
       bsSize="small" 
       className="ft-password" 
       componentClass="input" 
       onPaste={this.props.handleEmailPaste} 
       onChange={this.props.handlePasswordInput} 
       placeholder="Enter password" 
       style={{ width: 300}} 
       type="password" 
       value={this.props.password} 
      /> 
      <HelpBlock className="ft-password-help-text">{this.props.passwordHelpText}</HelpBlock> 
      </FormGroup> 
      <Button 
      className='ft-login-button' 
      type='submit' 
      >Login</Button> 
     </form> 
     </div>) 
    } 
} 

UPDATE

所以我加入这一点,这样可以解决问题:基本上这里

handleEmailInput(e) { 
    if(!this.state.email) { 
     this.setState({email: e.target.value}) 
    } 
    this.validateEmail() 
    } 

我说,嘿,如果有人在最​​初粘贴它,它将击中handleEmailPaste这setState的电子邮件,所以如果是这样的话(我知道handleEmailInput也将由该粘贴/更改触发),那么我不想再设置状态,如果handleEmailPaste已经设置它。相反,如果用户输入值而不是粘贴它,那么这个if语句将是bypassed,因此handleEmailInput would setState in that case

但我的整个电子邮件输入处理只是对我感到羞耻。如果你认为这是一个黑客,并有更好的重构这个代码的想法让我知道。

更新#2

废话,我注意到,我没有得到复制,但现在我不能输入Email地址框中输入新值,它不会让我和它只是坐在那里与填充电子邮件,但我不能修改它。

更新#3 (咆哮:凭啥不计算器使身体后textarea的更大的高度,我几乎看不到什么,我这里正在做的,当我滚动)

所以真的,我的问题不再是它复制粘贴文本的问题。我回到了原来的问题,我原来的问题是我能够粘贴一些东西,但它使输入无效,我无法弄清楚原因。我想我应该更新这篇文章的标题,但哦。

无论如何,我已经诉诸于只使用onChange并摆脱onPaste。没有更多的dups发生

所以问题:是与最初的行为,当你第一次粘贴到电子邮件文本框的值。我看到的行为(以下是没有onPaste更新的代码了)当你在第一时间的值粘贴:

  1. 当你在第一时间贴,它击中我handleEmailInput()方法。
  2. handleEmailInput调用setState({email: e.target.value})所以你会认为现在是设置this.state.email
  3. 但当handleEmailInput电话validateEmail()后,validateEmail,它会检查this.state.email和它的仍然""出于某种原因。所以它因此结果达到第二个setState,它将其设置为无效

不会在第一次调用this.setState({email: e.target.value })设置this.state.email到粘贴的电子邮件?我知道,当我把一个断点上线,e.target.value确实有我粘贴的电子邮件,但this.setState({email: e.target.value })完成调用,出于某种原因在validateEmail(时),它仍然得到""为this.state.email我不明白为什么。也许这是React及其生命周期的基础?或者我还没有意识到的其他一些基本问题......不确定。

LoginContainer (我已经完全去除onPaste逻辑)

import { connect } from 'react-redux' 
import React, { Component } from 'react' 
const zxcvbn = require('zxcvbn'), 
    _ = require('lodash') 

import * as AsyncActions from '../actions/Auth/AuthAsyncActions' 
import Login from '../components/Login/Login' 

class LoginContainer extends Component { 
    constructor(props) { 
    super(props) 
    this.state = { 
     email: '', 
     password: '', 
     errorMessage: '', 
     emailValidationState: null, 
     formIsValid: false, 
     formValidationState: null, 
     passwordValidationState: null, 
     passwordIsValid: null 
    } 

    this.handleEmailInput = this.handleEmailInput.bind(this) 
    this.handlePasswordInput = this.handlePasswordInput.bind(this) 
    this.handleLoginPressed = this.handleLoginPressed.bind(this) 
    this.resetFields = this.resetFields.bind(this) 
    this.validateForm = this.validateForm.bind(this) 
    this.validateEmail = this.validateEmail.bind(this) 
    this.validatePassword = this.validatePassword.bind(this) 
    } 

    handlePasswordInput(e) { 
    const password = e.target.value 
    this.setState({ password: password}) 
    this.validatePassword() 
    } 

    handleEmailInput(e) { 
    this.setState({email: e.target.value }) 
    this.validateEmail() 
    } 

    async handleLoginPressed(e) { 
    e.preventDefault() 
    this.validateForm() 

    await this.props.authenticate(this.state.email, this.state.password) 
    if(this.props.isAuthenticated) { 
     this.props.history.push('/dashboard') 
     return 
    } 

    if(!this.props.isAuthenticated){ 
     this.setState({ 
     formValidationState: 'error', 
     errorMessage: this.state.formIsValid && 
     'Your password and/or email is not associated with an active user' 
     }) 

     if(this.state.email && this.state.password){this.resetFields()} 
    } 
    } 

    validateForm(){ 
    this.validateEmail() 
    this.validatePassword() 
    this.setState({ 
     formIsValid: (this.state.emailValidationState === 'success' 
     && this.state.passwordValidationState === 'success')}) 
    } 

    validatePassword(){ 
    const password = zxcvbn(this.state.password) 
    if(password.score >=0){ 
     this.setState({ 
     passwordValidationState: 'error', 
     passwordHelpText: password.feedback.suggestions}) 
     return 
    } 

    this.setState({ 
     passwordValidationState: 'success', 
     passwordHelpText: null }) 
    } 

    validateEmail(){ 
    if(!_.isEmpty(this.state.email)) { 
     this.setState({ 
     emailValidationState: 'success', 
     emailError: '' 
     }) 

     return 
    } 

     this.setState({ 
     emailValidationState: 'error', 
     emailError: 'please enter an email address' 
     }) 
    } 

    resetFields(){ 
    this.setState({ 
     email: '', 
     emailError: '', 
     emailValidationState: null, 
     password: '', 
     passwordHelpText: '', 
     passwordValidationState: null }) 
    } 

    render(){ 
    return(
     <div> 
     <Login 
      email={this.state.email} 
      emailError={this.state.emailError} 
      emailValidationState={this.state.emailValidationState} 
      errorMessage={this.state.errorMessage} 
      formValidationState={this.state.formValidationState} 
      handleEmailInput={this.handleEmailInput} 
      handlePasswordInput={this.handlePasswordInput} 
      login={this.handleLoginPressed} 
      password={this.state.password} 
      passwordHelpText={this.state.passwordHelpText} 
      passwordValidationState={this.state.passwordValidationState} 
     /> 
     </div> 
    ) 
    } 
} 

const mapStateToProps = state => ({ 
    isAuthenticating: state.auth.isAuthenticating, 
    isAuthenticated: state.auth.isAuthenticated, 
    token: state.auth.token 
}) 

export const mapDispatchToProps = { 
    authenticate: AsyncActions.authenticate 
} 

export { Login } 
export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer) 

import React, { Component } from 'react' 
import { 
    Button, 
    ControlLabel, 
    HelpBlock, 
    FormControl, 
    FormGroup, 
    PageHeader } from 'react-bootstrap' 

LoginForm的 (注意我们使出回只具有的onChange电子邮件)

export default class LoginForm extends Component { 
    render(){ 
    return (
     <div className='ft-login-form'> 
     <PageHeader className='ft-header'><small>Login</small></PageHeader> 
     <form onSubmit={this.props.login}> 
      <FormGroup validationState={this.props.formValidationState}> 
      <ControlLabel className="ft-form-error-message">{this.props.errorMessage}</ControlLabel> 
      </FormGroup> 
      <FormGroup controlId="formBasicText" validationState={this.props.emailValidationState}> 
      <ControlLabel>Email</ControlLabel> 
      <FormControl 
       bsSize="small" 
       className="ft-username" 
       componentClass="input" 
       onChange={this.props.handleEmailInput} 
       placeholder="Enter email" 
       style={{ width: 300}} 
       type="email" 
       value={this.props.email} 
      /> 
      <HelpBlock className="ft-email-error">{this.props.emailError}</HelpBlock> 
      </FormGroup> 
      <FormGroup validationState={this.props.passwordValidationState}> 
      <ControlLabel>Password</ControlLabel> 
      <FormControl 
       bsSize="small" 
       className="ft-password" 
       componentClass="input" 
       onPaste={() => this.props.handleEmailPaste} 
       onChange={() => this.props.handlePasswordInput} 
       placeholder="Enter password" 
       style={{ width: 300}} 
       type="password" 
       value={this.props.password} 
      /> 
      <HelpBlock className="ft-password-help-text">{this.props.passwordHelpText}</HelpBlock> 
      </FormGroup> 
      <Button 
      className='ft-login-button' 
      type='submit' 
      >Login</Button> 
     </form> 
     </div>) 
    } 
} 
+0

你不能修改它,因为你的'email'是一个[受控组件](https://facebook.github.io/react/docs/forms.html#controlled-components),它的值取决于'state',其中,因为'如果',不更新 –

+0

好..如果我拿出如果,然后它复制该框中的值 – PositiveGuy

+0

是啊我卡住了然后如果是这样的话,因为我不知道如何摆脱原来的问题,那么如果是这样的话,而我粘贴在一封电子邮件,它dups它(不知何故它更新状态两次或什么,我不知道) – PositiveGuy

回答

1

很好,你意识到onPaste是错误的。这是解决方案的一半。 :)

你仍然缺少的一块是setState是一个异步功能。每当你打电话给setState,你只是在排队新的状态数据。使反应如此强大的部分原因在于它实际上具有固有的功能,可以采用多个功能并将它们合并为一个单独的更新(因此是对是否重新渲染的单独测试)。

也就是说,setState函数确实允许回调函数作为次要参数。使用这些回调来指定应用之后应执行的操作。它应该看起来像这样;

this.setState({email: e.target.value }, this.validateEmail) 
+1

没有看到这个我发布了相同的答案,因为你打字你的可能 – PositiveGuy

+0

是啊我认为它是syncronous ...哦!再次在这里反应n00b :) – PositiveGuy

+0

加上你更清洁! – PositiveGuy

0

是这里的n00b 。发现了问题:React - State not updated

解决方案:

handleEmailInput(e) { 
    this.setState({email: e.target.value },() => { 
     this.validateEmail() 
    }) 
    } 

现在,一旦排队的状态转变为真正作到更新this.state.email validateEmail只调用。

0

与#3相关:setState是异步的,所以不保证通过调用结束来更新状态。相反,你可以通过一个状态时,已更新,将调用回调:您正在使用“handleEmailPaste”您的密码输入栏

this.setState({email: value},() => { /* State is updated */ }); 

可能不会涉及您的问题,但。

此外,多次调用setState不会附加文本,所以即使使用相同的值调用两次,也不会重复该值。更可能的是,在这种情况下,从FormControl传递的值是错误的。我会在“输入”和“粘贴”回调中添加一个断点/ console.log,以检查FormControl传递的值。

最后(也与您的问题无关),在#3中,您的密码事件不应该是“onEvent = {()=> this.props.handleXyz}”。这不会调用回调。相反,它应该是onEvent = {this.props.handleXyz}。