2017-06-15 87 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}。