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

在下面的组件中,输入字段在键入字符后失去焦点。在使用 Chrome 的检查器时,看起来整个表单都被重新呈现了,而不仅仅是输入框的 value 属性。

我没有得到任何错误从埃斯林特或铬检查器。

提交表单本身的工作方式与实际输入字段的工作方式一样,无论是在呈现的返回中,还是作为单独的组件导入时,都是如此,而不是我在下面对其进行编码的方式。

为什么会这样?

主页组件

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;
133291 次浏览

事情是这样的:

当 onChange 事件触发时,回调调用带有新 title 值的 setState,该值作为道具传递给文本字段。此时,React 呈现一个新组件,这就是为什么您会失去焦点。

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

编辑:

请参阅关于 key: https://reactjs.org/docs/lists-and-keys.html#keys的文档

例如:

    <TextField
key="password" // <= this is the solution to prevent re-render
label="eMail"
value={email}
variant="outlined"
onChange={(e) => setEmail(e.target.value)}
/>

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

每次你的状态/道具改变,函数返回一个新的形式。它使你失去焦点。

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

<main id="main" role="main">
<div className="container-fluid">
<FormPostSingle />
</div>
</main>

= = >

<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>

我的问题是它在同一个文件中的无状态组件中重新呈现。所以一旦我去掉了那个不必要的无状态组件,直接把代码放进去,我就没有不必要的重新呈现了

render(){
const NewSocialPost = () =>
<div className='new-post'>
<input
onChange={(e) => this.setState({ newSocialPost: e.target.value })}
value={this.state.newSocialPost}/>
<button onClick={() => this._handleNewSocialPost()}>Submit</button>
</div>


return (
<div id='social-post-page'>
<div className='post-column'>
<div className='posts'>
<Stuff />
</div>
<NewSocialPost />
</div>
<MoreStuff />
</div>

我无权发表评论,那么这一定是一个答案。我也有类似的问题,亚历克斯 · 严的回答是正确的。

也就是说,我有那个功能

const DisplaySearchArea =()=>{return (arrayOfSearchFieldNames.map((element, index)=>{return(<div key ={index} className = {inputFieldStyle}><input  placeholder= {arrayOfPlaceholders[index]} type="text" className='border-0'
value={this.state[element]}
onChange={e => {this.setState({ [element]: e.target.value }); console.log(e.target)}}
onMouseEnter={e=>e.target.focus()}/></div>)}))}

当呈现为 <DisplaySearchArea />时,在 FF 中表现正常,而在 Chrome 中表现不正常 当渲染为{ ... }时,两者都可以。这不是那么“美丽”的代码,但工作,我已经被告知有过度使用 lambdas 的倾向。

我是“反应”的新手,遇到过这种情况。

以下是我解决的问题:

  1. 首先将所有组件移动到组件文件夹中,然后将它们导入到希望使用它们的位置
  2. 确保所有表单元素都具有 nameid属性
  3. 确保所有组件在您沿着树向上走时都得到一个唯一的 key

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

我认为真正的问题是 React 正在重新呈现所有内容(如前所述) ,有时重新呈现发生在没有 key但需要 key的父组件上。

我的问题是与 ExpansionPanel组件包装我的自定义组件的形式输入。面板需要 key以及!

希望这能帮到其他人,这快把我逼疯了!

谢谢,亚历克斯,这样我就解决了我的问题:

constructor(props, context) {
...
this.FormPostSingle = this.FormPostSingle.bind(this);
}
FormPostSingle() {
const onChange = this.onChange;
const onSubmit = this.onSubmit;
const valueTitle = this.state.post.title;
return (
<form onSubmit={onSubmit}>
<InputText name="title" label="Title" placeholder="Enter a title" onChange={onChange} value={valueTitle} />
<InputSubmit name="Save" />
</form>        );
}
render() {
let FormPostSingle = this.FormPostSingle
return...
}

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

原因如下:

我用的是文本字段里的键。在同一个块中,我有一个输入字段用于更新同一文本字段的值。现在,因为组件键正在改变,所以要重新呈现 UI。所以注意力不集中。

从中可以学到什么:

不要使用不断变化的按键。

设置正确的 id,确保没有其他组件具有相同的 id,设置它唯一,并且它不应该在状态更新时更改,最常见的错误是在状态更新时用更改的值更新 id

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

<input key="random1" type="text" name="displayName" />

不能随机生成 key = “ Random 1”。 比如说,

<div key={uuid()} className='scp-ren-row'>

Uuid ()将为每个重新呈现生成一组新的字符串,这将导致输入失去焦点。

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

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

这样问题就解决了。

有相同的问题,并解决了它在一个快速和容易的方式: 只是调用与 {compName()}的组件,而不是 <compName />

例如,如果我们有:

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


const main = () => (
<foo param1={true} />
);

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

const main = () => (
{foo({param1: true})}
);

我有这个问题,它是由 react-bootstrap/Container造成的,一旦我摆脱了它,包括一个独特的关键,每个形式的元素,一切都很好。

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

// 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

当状态改变时,response 将呈现 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

我注意到,人们经常将本地使用的组件 在里面放在希望使用它的组件中。通常是为了利用函数作用域获得对父组件道具的访问权限。

const ParentComp = ({ children, scopedValue }) => {
const ScopedComp = () => (<div>{ scopedValue }</div>)
return <ScopedComp />
}

我从来没有真正想过为什么需要这样做,因为你可以只是道具钻到内部功能的道具,并外部化它从母公司。

这个问题是一个很好的例子,说明了为什么应该始终将组件彼此外部化,即使它们用在一个模块中。另外,您总是可以使用智能文件夹结构来保持附近的东西。

src/
components/
ParentComp/
ParentComp.js
components/
ScopedComp.js

我的情况是,我把这个戴在一个孩子身上,

  //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/>
.
</>
)

你解出来了。 . . .

  //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
.
</>
)

对于那些反应本机面临的问题,文本输入失去焦点后,键入单个字符。 尝试将 onChangeText 传递给 TextInput 组件。 例如:

const [value, setValue] = useState("")
const onChangeText = (text) => {
setValue(text)
}
return <TextInput value={value} onChangeText={onChangeText} />

添加另一个答案: 当我在另一个组件中返回一个高阶组件时发生了这种情况:

/* A function that makes a higher order component */
const makeMyAwesomeHocComponent = <P, >(Component: React.FC<P>) => {
const AwesomeComponent: React.FC<P & AwesomeProp> = (props) => {
const { awesomeThing, ...passThroughProps } = props;


return (
<strong>Look at: {awesomeThing}!</strong>
<Component {...passThroughProps} />
);


}


return AwesomeComponent;
}


/* The form we want to render */
const MyForm: React.FC<{}> = (props) => {


const MyAwesomeComponent: React.FC<TextInputProps & AwesomeProp> =
makeMyAwesomeHocComponent(TextInput);


return <MyAwesomeComponent awesomeThing={"cat"} onChange={() => { /* whatever */ }} />
}


移动调用来创建正在呈现的事物之外的高阶组件。

const makeMyAwesomeHocComponent = <P, >(Component: React.FC<P>) => {
const AwesomeComponent: React.FC<P & AwesomeProp> = (props) => {
const { awesomeThing, ...passThroughProps } = props;


return (
<strong>Look at: {awesomeThing}!</strong>
<Component {...passThroughProps} />
);


}


return AwesomeComponent;
}


/* We moved this declaration */
const MyAwesomeComponent: React.FC<TextInputProps & AwesomeProp> =
makeMyAwesomeHocComponent(TextInput);


/* The form we want to render */
const MyForm: React.FC<{}> = (props) => {
return <MyAwesomeComponent awesomeThing={"cat"} onChange={() => { /* whatever */ }} />
}


基本上创建一个 ref 并将其分配给 input 元素

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} />

这将在键入时保持输入元素的焦点。

加上

autoFocus="autoFocus"

input的工作为我工作

<input
type="text"
autoFocus="autoFocus"
value = {searchString}
onChange = {handleChange}
/>

这个问题的解决方案是使用 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}
/>
);
},[]);

希望它能解决你的问题

问题是由 useState()函数引起的动态呈现() ,因此您可以这样做,例如。 在这段代码中,您应该使用 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>)
    

}

模范家长

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

这是一个很棒的问题,我也有同样的问题,分为三个部分。

  1. 随机生成密钥。
  2. 事件类型错误。
  3. 反应错误的 JSX 属性。

键: 当您使用随机键时,每个重新呈现都会导致失去焦点(键 = { Math.Random () * 36.4621596072})。

EventType: onChange 在每次击键时都会导致重新呈现,但是这也会导致问题。OnBlur 更好,因为它在单击输入外部之后进行更新。除非您想要将输入“绑定”到屏幕上的某个东西(可视化构建器) ,否则应该使用 onBlur 事件。

属性: JSX 不是 HTML,它有自己的属性(className,...)。 与其使用 value,不如在输入中使用 defaultValue = { foo }。

一旦我改变了这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)}/>

我也面临着同样的问题,一旦我输入任何字符,它就会失去焦点。添加 autoFocus 道具帮助我解决了这个问题。

代码片段

如果您正在为应用程序的设计系统开发原子组件,您可能会遇到这个问题。

考虑以下 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元素似乎是一个简单的优化。但是,任何时候,这个组件的父级重新呈现,这个组件重新呈现,当 response 在树中看到 <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. 将动态组件移到函数之外
  2. 用 useMemo 函数包装
const getComponent = (step) =>
dynamic(() => import(`@/components/Forms/Register/Step-${step}`), {
ssr: false,
});

并通过包装 useMemo 在组件内调用这个函数:

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

我做了一个 useRef的输入和 useEffect

对我来说,这是发生在内部的材质 UI 标签。我有一个搜索输入过滤器,过滤下面的表记录。搜索输入和表格都在标签内,每当输入一个字符时,输入就会失去焦点(显然是因为重新呈现的原因,整个东西都在标签内)。

我使用 useRef钩子作为输入字段参考,然后在我的 useEffect 中,每当数据列表发生变化时,我就触发输入的 focus。请参阅下面的代码

const searchInput = useRef();


useEffect(() => {
searchInput.current.focus();
}, [successfulorderReport]);

我在功能组件中使用 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;

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

解决方案 -

  1. 向输入元素添加一个 unique键,因为它有助于 React 识别哪个项目已经更改(和解)。确保您的关键不应该改变,它必须是 constant以及 unique

  2. 如果您在一个反应组件中定义一个 样式化组件样式化组件。如果您的输入元素位于该样式化组件内部,则在输入组件外部定义该样式化组件。否则,对于主组件的每个状态更改,它都会重新呈现您的样式化组件和输入,并且会失去焦点。

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


const Container = styled.div`
padding: 1rem 0.5rem;
border: 1px solid #000;
`;


function ExampleComponent() {
// Container styled component should not be inside this ExampleComponent
const [userName, setUserName] = useState("");


const handleInputChange = event => {
setUserName(event.target.value);
};


return (
<React.Fragment>
<Container> {/* Styled component */}
<input
key="user_name_key" // Unique and constant key
type="text"
value={userName}
onChange={handleInputChange}
/>
</Container>
</React.Fragment>
);
}


export default ExampleComponent;


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

我正在使用 Materials-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>
</>
);
};

我无法找到任何人与我的具体问题在线,这是一个帖子,出现在我的搜索顶部,所以我想留在这里。干杯

如果使用多个字段——不管出于什么原因动态地添加和删除它们 ——您可以使用自动对焦。但是,你必须自己跟踪焦点。或多或少像这样:

focusedElement = document.activeElement.id;
[…]


const id = 'dynamicField123'; // dynamically created.
<Input id={id} key={id} {...(focusedElement === id ? { autoFocus: true } : {})} />