在React ES6中,为什么输入字段在输入字符后会失去焦点?

spu*_*nge 20 text focus input ecmascript-6 reactjs

在下面的组件中,输入字段在输入字符后失去焦点.在使用Chrome的Inspector时,看起来整个表单正在重新呈现,而不是在输入时仅输入字段的value属性.

我从eslint和Chrome Inspector都没有错误.

提交表单本身的工作方式与实际输入字段的工作方式相同,当它位于渲染的返回中或作为单独的组件导入时,而不是如何在下面编码.

为什么会这样?

主页组件

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actionPost from '../redux/action/actionPost';
import InputText from './form/InputText';
import InputSubmit from './form/InputSubmit';

class _PostSingle extends Component {
    constructor(props, context) {
        super(props, context);
        this.state = {
            post: {
                title: '',
            },
        };
        this.onChange = this.onChange.bind(this);
        this.onSubmit = this.onSubmit.bind(this);
    }
    onChange(event) {
        this.setState({
            post: {
                title: event.target.value,
            },
        });
    }
    onSubmit(event) {
        event.preventDefault();
        this.props.actions.postCreate(this.state.post);
        this.setState({
            post: {
                title: '',
            },
        });
    }
    render() {
        const onChange = this.onChange;
        const onSubmit = this.onSubmit;
        const valueTitle = this.state.post.title;
        const FormPostSingle = () => (
            <form onSubmit={onSubmit}>
                <InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
                <InputSubmit name="Save" />
            </form>
        );
        return (
            <main id="main" role="main">
                <div className="container-fluid">
                    <FormPostSingle />
                </div>
            </main>
        );
    }
}

_PostSingle.propTypes = {
    actions: PropTypes.objectOf(PropTypes.func).isRequired,
};

function mapStateToProps(state) {
    return {
        posts: state.posts,
    };
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(actionPost, dispatch),
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(_PostSingle);
Run Code Online (Sandbox Code Playgroud)

文本输入组件

import React, { PropTypes } from 'react';

const InputText = ({ name, label, placeholder, onChange, value, error }) => {
    const fieldClass = 'form-control input-lg';
    let wrapperClass = 'form-group';
    if (error && error.length > 0) {
        wrapperClass += ' has-error';
    }
    return (
        <div className={wrapperClass}>
            <label htmlFor={name} className="sr-only">{label}</label>
            <input type="text" id={name} name={name} placeholder={placeholder} onChange={onChange} value={value} className={fieldClass} />
            {error &&
                <div className="alert alert-danger">{error}</div>
            }
        </div>
    );
};

InputText.propTypes = {
    name: PropTypes.string.isRequired,
    label: PropTypes.string.isRequired,
    placeholder: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired,
    value: PropTypes.string,
    error: PropTypes.string,
};

InputText.defaultProps = {
    value: null,
    error: null,
};

export default InputText;
Run Code Online (Sandbox Code Playgroud)

提交按钮组件

import React, { PropTypes } from 'react';

const InputSubmit = ({ name }) => {
    const fieldClass = 'btn btn-primary btn-lg';
    return (
        <input type="submit" value={name} className={fieldClass} />
    );
};

InputSubmit.propTypes = {
    name: PropTypes.string,
};

InputSubmit.defaultProps = {
    name: 'Submit',
};

export default InputSubmit;
Run Code Online (Sandbox Code Playgroud)

San*_*ane 32

这发生在我身上,虽然我设置了钥匙!

原因如下:

我正在使用文本字段中的键。在同一个区块内;我有一个输入字段来更新同一文本字段的值。现在,由于组件键发生变化,react 会重新渲染 UI。因此失去焦点。

从中得到什么:

不要使用不断变化的键。

  • 我也有类似的情况。我通过 nanoid 使用自动生成的密钥作为我的输入组件。每次重新渲染都会给它一个新的密钥。将其更改为在重新渲染时保持不变的关键点,并且它开始工作 - 不再失去焦点。 (3认同)
  • 我有额外的动态键:-)基于输入条件键 - 这就是我的原因 (2认同)

flo*_*oss 27

通过增加

autoFocus="autoFocus"
Run Code Online (Sandbox Code Playgroud)

input为我工作的时候

<input
  type="text"
  autoFocus="autoFocus"
  value = {searchString}
  onChange = {handleChange}
/>
Run Code Online (Sandbox Code Playgroud)

  • 不会因为反应重新渲染输入元素而失去焦点吗?这只是掩盖了实际问题。 (16认同)
  • 如果我们在子组件中有多个输入,则此方法不起作用 (15认同)
  • 不会,会有多个输入字段,但需要关注哪些用户正在输入@flos (3认同)

Ser*_*ati 17

遇到了同样的问题并以一种快速简单的方式解决了它:只需调用组件{compName()}而不是<compName />

例如,如果我们有:

const foo = ({param1}) => {
   // do your stuff
   return (
      <input type='text' onChange={onChange} value={value} />
   );
};

const main = () => (
   <foo param1={true} />
);
Run Code Online (Sandbox Code Playgroud)

然后,我们只需要改变我们调用foo()组件的方式:

const main = () => (
   {foo({param1: true})}
);
Run Code Online (Sandbox Code Playgroud)

  • 多么奇怪和烦人,谢谢你的提示,这对我也有用!我之前使用的是 foo(),但将其更改为 &lt;Foo /&gt;。我必须把它改回来才能让我的输入再次发挥作用。 (2认同)
  • 注意,如果你这样做并且你在 `foo` 中有一个状态变量,你的父级将在每次 foo 的状态改变时重新渲染。 (2认同)
  • 非常感谢。这让我发疯,所以我必须研究为什么会发生这种情况。如果您仍然感兴趣,这里有一个解释@Normal:[链接](https://dev.to/igor_bykov/react-calling-functions-components-as-functions-1d3l) (2认同)

Ezr*_*ang 13

这是怎么回事:

当你的onChange事件触发时,回调调用带有新标题值的setState,该标题值作为prop传递给你的文本字段.那时,React重新渲染它,这就是你失去焦点的原因.

我的第一个建议是提供组件键,特别是表单和输入本身.密钥允许React通过渲染保留组件的标识.

  • 正确解释原因但关键建议并不能解决问题。对于那些无法弄清楚为什么解决方案对他们不起作用的人来说仅供参考。 (10认同)
  • 对于可能有帮助的人来说,这也可能是由于拥有不需要的密钥而导致的。我在表体上有 key={Math.random()} ,然后表体上有动态行,这导致了这个错误。 (5认同)
  • 具有不断变化的关键属性会导致组件始终重新渲染,而不是进行差异化并仅重新渲染更改的部分。 (3认同)

Net*_*ogi 13

styled-components在功能组件内部使用时我遇到了类似的问题。每次我输入字符时,自定义输入元素都会失去焦点。

经过对代码进行大量搜索和实验后,我发现styled-components每次输入字符时,功能组件内部都会重新渲染输入字段,因为模板文字语法使表单成为一个函数,尽管它看起来像 Devtools 中的表达式。@HenryMueller的评论有助于让我朝着正确的方向思考。

我将样式组件移到功能组件之外,现在一切正常。

 import React, { useState } from "react";
 import styled from "styled-components";

 const StyledDiv = styled.div`
  margin: 0 auto;
  padding-left: 15px;
  padding-right: 15px;
  width: 100%;
`;

const StyledForm = styled.form`    
  margin: 20px 0 10px;
`;

 const FormInput = styled.input`
  outline: none;
  border: 0;      
  padding: 0 0 15px 0;
  width: 100%;
  height: 50px;
  font-family: inherit;
  font-size: 1.5rem;
  font-weight: 300;
  color: #fff;
  background: transparent;
  -webkit-font-smoothing: antialiased;
`;

const MyForm = () => {
const [value, setValue] = useState<string>("");

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
   setValue(e.target.value);
} 

const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault();
  if(value.trim() === '') {
    return;
  }

  localStorage.setItem(new Date().getTime().toString(), JSON.stringify(value));
  setValue('');
}

 return (
  <StyledDiv>
     <StyledForm onSubmit={handleSubmit}>
        <FormInput type="text" 
        id="inputText" 
        name="inputText"            
        placeholder="What Do You Want To Do Next?"
        value={value}
        onChange={handleChange}/>
    </StyledForm>
   </StyledDiv>
  )
 }

export default MyForm;
Run Code Online (Sandbox Code Playgroud)

styled-components在这种情况下,最好的使用方法是将它们保存在单独的文件中并导入它们。

  • 我对你感激不尽。这是我的样式组件问题。我通过将样式组件放置在我的函数之外解决了这个问题。我也可以将它放在单独的文件中。非常感谢您发布此内容。 (2认同)

小智 10

您必须为输入组件使用唯一键。

<input key="random1" type="text" name="displayName" />
Run Code Online (Sandbox Code Playgroud)

key="random1" 不能随机生成。例如,

<div key={uuid()} className='scp-ren-row'>
Run Code Online (Sandbox Code Playgroud)

uuid() 将为每次重新渲染生成一组新的字符串。这将导致输入失去焦点。

如果元素是在 .map() 函数中生成的,则使用索引作为键的一部分。

{rens.map((ren,i)=>{
    return(
    <div key={`ren${i+1}`} className='scp-ren-row'>
       {ren}{i}
</div>)
}
Run Code Online (Sandbox Code Playgroud)

这将解决问题。


Ale*_*Yan 6

这是因为您正在render()中的函数中呈现表单。

每次状态/属性更改时,该函数都会返回一个新表格。它导致您失去焦点。

尝试将函数内部的内容直接放入渲染中。

       <main id="main" role="main">
            <div className="container-fluid">
                <FormPostSingle />
            </div>
        </main>
Run Code Online (Sandbox Code Playgroud)

====>

       <main id="main" role="main">
            <div className="container-fluid">
                <form onSubmit={onSubmit}>
                    <InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
                    <InputSubmit name="Save" />
                </form>
            </div>
        </main>
Run Code Online (Sandbox Code Playgroud)

  • 对于功能组件人员来说,这意味着不要在功能组件体内定义组件...本质上每个组件都将位于模块的顶层(即使一个模块中有多个组件)...我在函数中使用样式组件定义时遇到了这个问题。比较。它_看起来_像一个表达式,但当然它实际上是在奇怪的(对我来说)样式的组件模板文字语法中调用函数。哎呀! (8认同)
  • 所以我猜你是对的,但是你不能直接放入元素的原因很可能是因为你这样做的全部原因是为了获得多个元素的重用。如果您想要多个“FormPostSingle”组件,您会怎么做? (7认同)
  • 这个答案很有帮助,我把它变成了像“{FormPostSingle(props)}”这样的函数调用,它立即起作用了。我不知道你在谈论渲染函数,因为我使用钩子 (5认同)
  • 不知道为什么这在正确时被否决了。正是匿名函数 FormPostSingle 导致了这个问题,因为它在 render 内部声明了一个具有新函数签名的新函数,每次渲染运行时都会创建一个新函数,这导致 React 无法跟踪它创建的元素。 (2认同)
  • @Nateous我的意思是。“不要在渲染函数下创建表单函数”,如果要重用表单组件,则应创建一个单独的函数/组件文件并使用它。 (2认同)

小智 6

我也有这个问题,我的问题与使用另一个组件来包装 textarea 有关。

// example with this problem
import React from 'react'

const InputMulti = (props) => {
  const Label = ({ label, children }) => (
    <div>
      <label>{label}</label>
      { children }
    </div>
  )

  return (
    <Label label={props.label}>
      <textarea
        value={props.value}
        onChange={e => props.onChange(e.target.value)}
      />
    </Label>
  )
}

export default InputMulti
Run Code Online (Sandbox Code Playgroud)

当状态改变时,react 会渲染InputMulti组件,每次都会重新定义 Label 组件,这意味着输出在结构上是一样的,但是因为 JS,函数会被考虑!=

我的解决方案是将Label组件移到组件之外InputMulti,使其保持静态。

// fixed example
import React from 'react'

const Label = ({ label, children }) => (
  <div>
    <label>{label}</label>
    { children }
  </div>
)

const InputMulti = (props) => {
  return (
    <Label label={props.label}>
      <textarea
        value={props.value}
        onChange={e => props.onChange(e.target.value)}
      />
    </Label>
  )
}

export default InputMulti
Run Code Online (Sandbox Code Playgroud)

我注意到人们经常将本地使用的组件放在想要使用它的组件中。通常利用函数作用域并获得对父组件 props 的访问权限。

const ParentComp = ({ children, scopedValue }) => {
  const ScopedComp = () => (<div>{ scopedValue }</div>)
  return <ScopedComp />
}
Run Code Online (Sandbox Code Playgroud)

我从来没有真正想过为什么需要这样做,因为您可以将道具钻到内部函数中,然后从父组件中将其外部化。

这个问题是一个完美的例子,说明为什么您应该始终将组件相互外部化,即使它们在一个模块中使用。此外,您始终可以使用智能文件夹结构来保持附近的事物。

src/
  components/
    ParentComp/
      ParentComp.js
      components/
        ScopedComp.js
Run Code Online (Sandbox Code Playgroud)