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

235
在我的下面组件中,在键入字符后,输入字段会失去焦点。使用Chrome Inspector时,看起来整个表单在键入时被重新渲染,而不仅仅是输入字段的值属性。
我从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);

文本输入组件

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;

提交按钮组件

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;

1
感谢这个问题,它是从 https://www.paypal-community.com/t5/Managing-Account/Enter-your-code-page-input-looses-focus/m-p/2876787/highlight/true#M21997 链接过来的,因为这可能与为什么 PayPal 的双重认证开始失去焦点有关... - Tobias Kienzler
34个回答

2

我刚接触React,一直遇到这个问题。

以下是我的解决方法:

  1. 首先将所有组件移动到components文件夹中,然后在需要使用它们的地方导入
  2. 确保所有表单元素都有nameid属性
  3. 确保所有组件在向上遍历树时都有唯一的key

比我聪明的人可能会告诉我们为什么可以跳过第一步并保持一切内联,但这只是帮助我组织代码的方法。

我认为真正的问题是React正在重新渲染所有内容(如已经说明的那样),有时重新渲染发生在一个没有key但需要一个的父组件上。

我的问题出在ExpansionPanel组件包装我的自定义表单输入组件上。这些面板也需要key

希望这能帮助其他人,这让我疯狂了!


2

我曾经遇到过同样的问题,只要我输入任何字符,就会失去焦点。添加autoFocus属性帮助我解决了这个问题。

TypeScript代码片段


4
如果表单中只有一个输入字段,那么这是可以的。 但是,如果您有多个输入字段,则这将无法按预期工作。 - Musadul Islam

2

解决这个问题的方法是使用 useCallback。它用于记忆函数,这意味着它缓存给定一组输入参数的函数的返回值。

const InputForm = useCallback(({ label, lablevalue, placeholder, type, value,setValue }) => {
  return (
      <input
        key={label}
        type={type}
        value={value}
        onChange={(e) => setIpValue(e.target.value)}
         placeholder={placeholder}
      />
      );
},[]);

希望它能解决你的问题。

遗憾的是,在这种情况下这并不会有太大的帮助。首先,您忘记将依赖键添加到数组中,因此输入值不会随着他们的输入而更新。其次,如果您添加了依赖键,则值将发生更改,所以组件将重新渲染,尽管使用了useCallback - Brook Jordan

2

问题出在由useState()函数引起的动态渲染(render()),您可以通过以下方式解决。

在此代码中,您应该使用onChange()仅获取新的更新数据,并使用onMouseLeave()处理更新,但前提是数据已更改以获得更好的性能。

示例子组件

        export default function Child(){
        const [dataC,setDataC]=useState()
        return(<Grid>
        <TextField 
        .
        .
        onChange={(r)=> setDataC(r.target.value) }
        onMouseLeave={(e)=> {   
          if(dataC!=props.data) { // to avoid call handleupdate each time you leave the textfield
            props.handlechange(e.target.value)  // update partent.data 
          }
        }
            
        />
        
        </Grid>)
    
    }

exmple parent

export default function Parent(){
const [data,setData]=useState()
return(
 <Grid>
    <Child handlechange={handlechanges} data={data}/>
 </Grid>)
}

1
我做了以下步骤:
  1. 将动态组件移出函数
  2. 使用useMemo函数包裹
const getComponent = (step) =>
 dynamic(() => import(`@/components/Forms/Register/Step-${step}`), {
   ssr: false,
 });

通过使用useMemo将此函数包装在组件内部,并调用该函数:

const CurrentStep = useMemo(() => getComponent(currentStep), currentStep]);

1
如果您正在为应用程序的设计系统开发原子组件,那么您可能会遇到以下问题。
考虑以下“Input”组件:
export const Input = forwardRef(function Input(
  props: InputProps,
  ref: ForwardedRef<HTMLInputElement>,
) {
  const InputElement = () => (
    <input ref={ref} {...props} />
  );

  if (props.icon) {
    return (
      <span className="relative">
        <span className="absolute inset-y-0 left-0 flex items-center pl-2">
          <label htmlFor={props.id} className="p-1 cursor-pointer">
            {icon}
          </label>
        </span>
        <InputElement />
      </span>
    );
  } else {
    return <InputElement />;
  }
});

一开始,重复使用input元素似乎是一种简单的优化。然而,每当此组件的父级重新渲染时,该组件也会重新渲染,并且当React在树中看到<InputElement />时,它也将呈现一个新的<input>元素,因此现有元素将失去焦点。
你的选择是:
  1. 使用useMemo对组件进行记忆
  2. 在条件渲染的两个分支中都定义<input>元素的代码。在这种情况下,由于<input>元素相对较简单,所以可以这样做。更复杂的组件可能需要选项1。
因此,你的代码变成了:
export const Input = forwardRef(function Input(
  props: InputProps,
  ref: ForwardedRef<HTMLInputElement>,
) {
  if (props.icon) {
    return (
      <span className="relative">
        <span className="absolute inset-y-0 left-0 flex items-center pl-2">
          <label htmlFor={props.id} className="p-1 cursor-pointer">
            {icon}
          </label>
        </span>
        <input ref={ref} {...props} />
      </span>
    );
  } else {
    return <input ref={ref} {...props} />;
  }
});

1

基本上创建一个引用并将其分配给输入元素

const inputRef = useRef(null); // Javascript

const inputRef = useRef<HTMLInputElement>(null); // Typescript

// In your Input Element use ref and add autofocus

<input ref={inputRef} autoFocus={inputRef.current === document.activeElement} {...restProps} />

在输入时,这会使输入元素保持焦点。


1

我很晚才来,但我已经追踪这个问题好几天了,最终解决了。希望能帮到别人。

我正在使用Material-ui的Dialog组件,并且希望在单击菜单项时显示对话框。例如:

import React, { useState } from "react";
import {
  Menu,
  MenuItem,
  Dialog,
  DialogContent,
  TextField,
} from "@mui/material";

const MyMenu = () => {
  const [open, setOpen] = useState(false);
  return (
    <Menu>
      <MenuItem>option 1</MenuItem>

      <MenuItem onClick={() => setOpen(!open)}>
        option 2
        <Dialog open={open}>
          <DialogContent>
            <TextField />
          </DialogContent>
        </Dialog>
      </MenuItem>
      
    </Menu>
  );
};


我遇到了TextField失去焦点的问题,但只有在按下asdcv键时才会出现。如果我按下这些键中的任何一个,它就不会在文本字段中输入任何内容,而是失去焦点。我修复问题的假设是一些菜单选项包含了这些字符,它会尝试切换焦点到其中一个选项。
我找到的解决方案是将对话框移动到Menu组件之外。
const MyMenu = () => {
  const [open, setOpen] = useState(false);
  return (
      <>
    <Menu>
      <MenuItem>option 1</MenuItem>

      <MenuItem onClick={() => setOpen(!open)}>
        option 2
      </MenuItem>

    </Menu>

    <Dialog open={open}>
      <DialogContent>
        <TextField />
      </DialogContent>
    </Dialog>
      </>
  );
};

我在网上找不到与我的问题相似的人,而这篇帖子是我搜索得到的最顶部的帖子,所以我想在这里留言。谢谢。


1
在我的情况下,这个内容是在一个子元素中的:

  //in fact is a constant
  const RenderOnDelete=()=>(
  <>  .
      .
      <InputText/>
      .
      .
  </>
)


//is a function that return a constant
const RenderOnRadioSelected=()=>{
  
  switch (selectedRadio) {
    case RADIO_VAL_EXIST:
           return <RenderOnExist/>
    case RADIO_VAL_NEW:
           return <RenderOnNew/>
    case RADIO_VAL_DELETE:
           return <RenderOnDelete/>
    default:
          return <div>Error</div>
  }
}

并且这是父级元素中的内容

return(
<> 
.
<RenderOnRadioSelected/>
.
</>
)

通过不调用组件,而是根据情况调用函数()或常量,Y解决了它。
  //in fact is a constant
  const RenderOnDelete=(
  <>  .
      .
      <InputText/>
      .
      .
  </>
)


//is a function that return a constant
const RenderOnRadioSelected=()=>{
  
  switch (selectedRadio) {
    case RADIO_VAL_EXIST:
           return {RenderOnExist}
    case RADIO_VAL_NEW:
           return {RenderOnNew}
    case RADIO_VAL_DELETE:
           return {RenderOnDelete}//Calling the constant
    default:
          return <div>Error</div>
  }
}

和这个在父级中

return(
<> 
.
{RenderOnRadioSelected()}//Calling the function but not as a component
.
</>
)

你好,欢迎来到Stack Overflow!如果你遇到了问题,请毫不犹豫地提出新的问题 - Saud

1

这是一个很好的问题,我也曾经遇到过相同的问题,它由三个部分组成。

  1. 随机生成的键。
  2. 错误的事件类型。
  3. 错误的React JSX属性。

键:当您使用随机键时,每次重新渲染都会导致React失去焦点(key={Math.random()*36.4621596072})。

事件类型:onChange每按一次键就会重新渲染,但这也可能会带来问题。 onBlur更好,因为它在单击输入框外部后更新。除非你想要将其“绑定”到屏幕上的某些内容(可视化建造者),否则应该使用onBlur事件。

属性:JSX不是HTML并且具有自己的属性(className,...)。在输入中,最好使用defaultValue={foo}而不是value。

一旦我改变了这3个东西,它就运行得很好。下面是示例。

父级:

const [near, setNear] = useState( "" );
const [location, setLocation] = useState( "" );
 <ExperienceFormWhere 
        slug={slug} 
        questionWhere={question_where} 
        setLocation={handleChangeSetLocation} 
        locationState={location} 
        setNear={setNear} 
        nearState={near} 
        key={36.4621596072}/>

子元素:

<input 
defaultValue={locationState} 
className={slug+"_question_where_select search_a_location"} 
onBlur={event => setLocation(event.target.value)}/>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接