【问题标题】:Wrapping a Component means it loses focus on rerender包装一个组件意味着它失去了对重新渲染的关注
【发布时间】:2017-04-12 15:08:35
【问题描述】:

我有一个很长的反应形式。之前,它有一堆这样定义的组件:

<input
  type='text'
  value={this.state.form.nameOfFormField}
  onChange={this.updateForm('nameOfFormField')} />

其中updateForm是(field) =&gt; (e) =&gt; {}形式的函数,方便代码复用。

我想让它更易于维护,所以我创建了一个组件,SpecialInput,它的定义如下:

const SpecialInputBuilder = (form, onChange) => ({ field, ..props }) => (
  <input
    type='text'
    value={form[field]}
    onChange={onChange(field)}
    {...props} />
)

现在,我可以像这样在渲染期间定义输入:

const SpecialInput = SpecialInputBuilder(this.state.form, this.updateForm)

并像这样在组件中使用它:

<SpecialInput field='nameOfFormField' />

显然,这要简洁得多。但这也意味着每次输入输入字段时(即调用updateForm 时)输入字段都会失去焦点,因为每次调用render 函数时都会定义SpecialInput。为每个元素定义一个key 似乎并不能缓解这个问题。如何在仍然使用这个更简单的组件的同时解决这个问题?有没有中间地带?

【问题讨论】:

  • 我能想到的唯一方法是将输入定义为反应类上的项目。也就是在 componentWillMount 你可以创建一个this.specialInput = SpecialInputBuilder(this.state.form, this.updateForm) 然后在你的渲染中使用已经定义的输入。

标签: javascript reactjs


【解决方案1】:

主要问题是您的 onChange 调用在渲染后立即执行,而不是在输入更改时对要调用的函数的引用。

// this executes immediately
onChange={onChange(field)}

// this is a reference to the function with a prop prepended
onChange={onChange.bind(this,field)}

【讨论】:

  • 我的问题是它是如何工作的?他之前的“工作”代码有onChange={this.updateForm('nameOfFormField')},无论如何都不应该工作。话虽如此,我认为这不是他报告的问题。这是因为在每个渲染周期都会创建一个新的输入对象,该对象会重置光标以及与之相关的所有内容
  • onChange 在我的代码中返回一个处理event 对象的函数,即(field) =&gt; (event) =&gt; {}。抱歉,我在最初的问题中没有说清楚 - 我现在会更新。
  • 如果不绑定它或创建一个空函数然后传递参数,您仍然无法使用参数调用它。
  • @Brand 你需要一个bind 和this 作为=&gt; onChange.bind(this,field, event) 上的第一个参数
  • updateForm 绑定在元素的构造函数中。我不确定您认为这里的问题是什么 - input 元素在展开时正常工作,也使用 onChange={this.updateForm('nameOfFormField')}。
【解决方案2】:

我有类似的方法,但最终将其更改为这个;

(1) Input typer 子组件:

import React, { Component } from 'react';

class FreeTextField extends Component {
  inputValueFn(e) {
    this.props.userInput(this.props.responseObject, e.target.value);
  }
  render() {
    return (
      <div className="input-group">
            <label>{this.props.buttonText ? this.props.buttonText : "Firstname"}</label>
            <input type={this.props.type} placeholder="" className="form-control" defaultValue={this.props.defaultValue} onChange={this.inputValueFn.bind(this)} />
      </div>
    );
  }
}

export default FreeTextField;

(2) 在父组件中你可以通过 props 指定所有相关的子属性

// import
import FreeTextField from './pathTo/FreeTextField';

// Initial state
this.state = {
responseObject: {}
}

// onChange it updates the responseObject
    userInput(fieldKey,value) {
        let responseObject = this.state.responseObject;
        responseObject[fieldKey] = value;
        this.setState({responseObject:responseObject});
    }

// component render()
<FreeTextField 
buttonText="First Name" 
type="text" 
formObjectKey="first_name" 
userInput{this.userInput.bind(this)} />

【讨论】:

    【解决方案3】:

    为什么不把你的输入构建器改成一个 React 组件呢?

    const SpecialInput = (props) => {
      return (
        <input
          value={props.form[props.field]}
          {...props}
          type={props.type || 'text'}
          onChange={() => props.onChange(props.field)} 
        />
      )
    }
    

    并以同样的方式使用它。

    <SpecialInput field='nameOfFormField' onChange={this.updateForm} form={this.state.form} />
    

    【讨论】:

    • 如果你这样定义SpecialInput,那么form 永远不会作为我可以看到的道具传递?
    • @KyleFahringer 抱歉,我忘记将它添加到您使用该组件的渲染部分。已更新。
    • 不管怎样,以这种方式使用组件的目的就被打败了——我每次使用SpecialInput 属性时仍然需要定义form 和onChange 属性,这就是我试图避免。虽然这在技术上可以解决问题,但基本上相当于在任何地方恢复使用input 元素。
    • @KyleFahringer 这是 React 的做事方式。使用我在评论中描述的您已经定义的唯一其他方法。您必须在 willMount 函数中定义这些输入,并在组件的整个生命周期中使用对这些输入的引用。如果您愿意,我可以尝试在我的答案中编写一个模拟解决方案
    猜你喜欢
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    • 2020-05-03
    • 2011-09-18
    • 1970-01-01
    • 2020-12-03
    • 2020-06-20
    • 2012-01-26
    相关资源
    最近更新 更多