Piszę tylko do wprowadzania tekstu i na onChange
wypadek setState
, gdyby zadzwoniłem , React ponownie renderuje mój interfejs użytkownika. Problem polega na tym, że wprowadzanie tekstu zawsze traci fokus, więc muszę go ponownie ustawić dla każdej litery: D.
var EditorContainer = React.createClass({
componentDidMount: function () {
$(this.getDOMNode()).slimScroll({height: this.props.height, distance: '4px', size: '8px'});
},
componentDidUpdate: function () {
console.log("zde");
$(this.getDOMNode()).slimScroll({destroy: true}).slimScroll({height: 'auto', distance: '4px', size: '8px'});
},
changeSelectedComponentName: function (e) {
//this.props.editor.selectedComponent.name = $(e.target).val();
this.props.editor.forceUpdate();
},
render: function () {
var style = {
height: this.props.height + 'px'
};
return (
<div className="container" style={style}>
<div className="row">
<div className="col-xs-6">
{this.props.selected ? <h3>{this.props.selected.name}</h3> : ''}
{this.props.selected ? <input type="text" value={this.props.selected.name} onChange={this.changeSelectedComponentName} /> : ''}
</div>
<div className="col-xs-6">
<ComponentTree editor={this.props.editor} components={this.props.components}/>
</div>
</div>
</div>
);
}
});
javascript
reactjs
Krab
źródło
źródło
Odpowiedzi:
Bez zobaczenia reszty kodu, to jest przypuszczenie. Podczas tworzenia EditorContainer określ unikalny klucz dla komponentu:
<EditorContainer key="editor1"/>
Kiedy nastąpi ponowne renderowanie, jeśli ten sam klucz zostanie zauważony, to powie Reactowi, aby nie blokował i nie generował widoku, zamiast tego użyć go ponownie. Następnie skupiony element powinien zachować ostrość.
źródło
Wciąż tu wracam i zawsze na końcu znajduję rozwiązanie mojego gdzie indziej. Więc udokumentuję to tutaj, ponieważ wiem, że znowu o tym zapomnę!
Przyczyną
input
utraty koncentracji w moim przypadku był fakt, że ponownie renderowałeminput
zmianę stanu włączenia.Kod buggy:
import React from 'react'; import styled from 'styled-components'; class SuperAwesomeComp extends React.Component { state = { email: '' }; updateEmail = e => { e.preventDefault(); this.setState({ email: e.target.value }); }; render() { const Container = styled.div``; const Input = styled.input``; return ( <Container> <Input type="text" placeholder="Gimme your email!" onChange={this.updateEmail} value={this.state.email} /> </Container> ) } }
Problem polega więc na tym, że zawsze zaczynam kodować wszystko w jednym miejscu, aby szybko przetestować, a później podzielić to wszystko na osobne moduły. Ale tutaj ta strategia działa odwrotnie, ponieważ aktualizacja stanu przy zmianie wejścia wyzwala funkcję renderowania, a fokus zostaje utracony.
Naprawa jest prosta, wykonaj modularyzację od początku, innymi słowy „Przenieś
Input
komponent zrender
funkcji”Kod stały
import React from 'react'; import styled from 'styled-components'; const Container = styled.div``; const Input = styled.input``; class SuperAwesomeComp extends React.Component { state = { email: '' }; updateEmail = e => { e.preventDefault(); this.setState({ email: e.target.value }); }; render() { return ( <Container> <Input type="text" placeholder="Gimme your email!" onChange={this.updateEmail} value={this.state.email} /> </Container> ) } }
Nr ref. do rozwiązania: https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947
źródło
render()
i zclass ... extends Component
powodu odwołania dothis
. np.onChange={this.handleInputChange}
render
funkcji, dla komponentów funkcjonalnych Reacta nie definiuje komponentów w ciele funkcji.render
funkcji, której przekazano metodę „this.handleChange” i zwróciły komponenty, które mają być użyte. Npconst TextAreaDataField = TextAreaDataFieldFactory(this.handleChange)
. Po prostu przeniosłem tworzenie komponentu do konstruktora i uzyskałem do nich dostęp wrender
metodzie jakothis.TextAreaDataField
. Działał jak urok.Jeśli problem występuje w routerze reagującym,
<Route/>
użyj właściwościrender
zamiastcomponent
.<Route path="/user" render={() => <UserPage/>} />
Utrata skupienia ma miejsce, ponieważ
component
rekwizyt używa zaReact.createElement
każdym razem, a nie tylko ponownie renderuje zmiany.Szczegóły tutaj: https://reacttraining.com/react-router/web/api/Route/component
źródło
<Route component={() => <MyComponent/>} />
kiedy powinienem był to zrobić<Route component={MyComponent} />
Moja odpowiedź jest podobna do tego, co powiedział @ z5h.
W moim przypadku
Math.random()
generowałem unikatkey
dla komponentu.Myślałem, że
key
jest on używany tylko do wyzwalania ponownego renderowania dla tego konkretnego komponentu, a nie do ponownego renderowania wszystkich komponentów w tej tablicy (zwracam tablicę komponentów w moim kodzie). Nie wiedziałem, że służy do przywracania stanu po ponownym renderowaniu.Usunięcie tego wykonało pracę za mnie.
źródło
Zastosowanie
autoFocus
atrybutu doinput
elementu może stanowić obejście problemu w sytuacjach, w których należy skoncentrować się tylko na jednym wejściu. W takim przypadkukey
atrybut byłby niepotrzebny, ponieważ jest to tylko jeden element, a ponadto nie musiałbyś się martwić o rozbicieinput
elementu na własny komponent, aby uniknąć utraty koncentracji na ponownym renderowaniu głównego komponentu.źródło
autofocus
do pola wprowadzania danych to początek. 🙌Mam takie samo zachowanie.
Problem w moim kodzie polegał na tym, że utworzyłem zagnieżdżoną tablicę elementów jsx w następujący sposób:
const example = [ [ <input value={'Test 1'}/>, <div>Test 2</div>, <div>Test 3</div>, ] ] ... render = () => { return <div>{ example }</div> }
Każdy element w tej zagnieżdżonej tablicy jest ponownie renderowany za każdym razem, gdy aktualizuję element nadrzędny. I tak wejścia tracą tam "ref" prop za każdym razem
Rozwiązałem problem z przekształceniem tablicy wewnętrznej na komponent reagujący (funkcja z funkcją renderowania)
const example = [ <myComponentArray /> ] ... render = () => { return <div>{ example }</div> }
EDYTOWAĆ:
Ten sam problem pojawia się, gdy buduję plik zagnieżdżony
React.Fragment
const SomeComponent = (props) => ( <React.Fragment> <label ... /> <input ... /> </React.Fragment> ); const ParentComponent = (props) => ( <React.Fragment> <SomeComponent ... /> <div /> </React.Fragment> );
źródło
Rozwiązałem ten sam problem, usuwając atrybut klucza w danych wejściowych i jego elementy nadrzędne
// Before <input className='invoice_table-input invoice_table-input-sm' type='number' key={ Math.random } defaultValue={pageIndex + 1} onChange={e => { const page = e.target.value ? Number(e.target.value) - 1 : 0 gotoPage(page) }} /> // After <input className='invoice_table-input invoice_table-input-sm' type='number' defaultValue={pageIndex + 1} onChange={e => { const page = e.target.value ? Number(e.target.value) - 1 : 0 gotoPage(page) }} />
źródło
Właśnie natknąłem się na ten problem i przyszedłem tutaj po pomoc. Sprawdź swój CSS! Pole wejściowe nie może mieć
user-select: none;
lub nie będzie działać na iPadzie.źródło
U mnie było to spowodowane tym, że pole wprowadzania wyszukiwania było renderowane w tym samym komponencie (zwanym UserList), co lista wyników wyszukiwania. Dlatego za każdym razem, gdy zmieniły się wyniki wyszukiwania, cały składnik UserList był ponownie renderowany, łącznie z polem wprowadzania.
Moim rozwiązaniem było stworzenie zupełnie nowego komponentu o nazwie UserListSearch, który jest niezależny od UserList . Nie musiałem ustawiać kluczy w polach wejściowych w UserListSearch, aby to zadziałało. Funkcja renderowania my UsersContainer wygląda teraz następująco:
class UserContainer extends React.Component { render() { return ( <div> <Route exact path={this.props.match.url} render={() => ( <div> <UserListSearch handleSearchChange={this.handleSearchChange} searchTerm={this.state.searchTerm} /> <UserList isLoading={this.state.isLoading} users={this.props.users} user={this.state.user} handleNewUserClick={this.handleNewUserClick} /> </div> )} /> </div> ) } }
Mam nadzieję, że to też komuś pomoże.
źródło
Jeśli pole wejściowe znajduje się wewnątrz innego elementu (tj. Elementu kontenera, takiego jak
<div key={"bart"}...><input key={"lisa"}...> ... </input></div>
- elipsy wskazujące tutaj pominięty kod), na elemencie kontenera (a także w polu wejściowym) musi znajdować się unikalny i stały klucz . W przeciwnym razie React renderuje zupełnie nowy element kontenera, gdy stan dziecka jest aktualizowany, a nie tylko ponownie renderuje stary kontener. Logicznie rzecz biorąc, należy zaktualizować tylko element podrzędny, ale ...Miałem ten problem podczas próby napisania komponentu, który pobierał wiele informacji adresowych. Działający kod wygląda następująco
// import react, components import React, { Component } from 'react' // import various functions import uuid from "uuid"; // import styles import "../styles/signUp.css"; export default class Address extends Component { constructor(props) { super(props); this.state = { address1: "", address2: "", address1Key: uuid.v4(), address2Key: uuid.v4(), address1HolderKey: uuid.v4(), address2HolderKey: uuid.v4(), // omitting state information for additional address fields for brevity }; this.handleChange = this.handleChange.bind(this); } handleChange(event) { event.preventDefault(); this.setState({ [`${event.target.id}`]: event.target.value }) } render() { return ( <fieldset> <div className="labelAndField" key={this.state.address1HolderKey} > <label className="labelStyle" for="address1">{"Address"}</label> <input className="inputStyle" id="address1" name="address1" type="text" label="address1" placeholder="" value={this.state.address1} onChange={this.handleChange} key={this.state.address1Key} ></input > </div> <div className="labelAndField" key={this.state.address2HolderKey} > <label className="labelStyle" for="address2">{"Address (Cont.)"}</label> <input className="inputStyle" id="address2" name="address2" type="text" label="address2" placeholder="" key={this.state.address2Key} ></input > </div> {/* omitting rest of address fields for brevity */} </fieldset> ) } }
Uważni czytelnicy zauważą, że
<fieldset>
jest to element zawierający, ale nie wymaga klucza. To samo dotyczy,<>
a<React.Fragment>
nawet<div>
dlaczego? Może tylko bezpośredni pojemnik potrzebuje klucza. Nie wiem. Jak mówią podręczniki matematyki, wyjaśnienie pozostawia się czytelnikowi jako ćwiczenie.źródło
Głównym powodem jest to, że po ponownym wyrenderowaniu Reacta poprzedni ref DOM będzie nieprawidłowy. To znaczy, że reakcja zmieniła drzewo DOM, a ty this.refs.input.focus nie będzie działać, ponieważ wejście tutaj już nie istnieje.
źródło
Udzielone odpowiedzi nie pomogły mi, oto co zrobiłem, ale miałem wyjątkową sytuację.
Aby wyczyścić kod, zwykle używam tego formatu, dopóki nie będę gotowy do przeciągnięcia komponentu do innego pliku.
render(){ const MyInput = () => { return <input onChange={(e)=>this.setState({text: e.target.value}) /> } return( <div> <MyInput /> </div> )
Ale to spowodowało, że stracił ostrość, kiedy umieściłem kod bezpośrednio w div.
return( <div> <input onChange={(e)=>this.setState({text: e.target.value}) /> </div> )
Nie wiem, dlaczego tak jest, to jedyny problem, jaki miałem z pisaniem tego w ten sposób i robię to w większości plików, które mam, ale jeśli ktoś robi coś podobnego, to traci koncentrację.
źródło
zmieniłem
value
prop nadefaultValue
. To działa dla mnie.... // before <input value={myVar} /> // after <input defaultValue={myVar} />
źródło
Miałem ten sam problem z tabelą html, w której mam linie tekstu wejściowego w kolumnie. wewnątrz pętli czytam obiekt json i tworzę wiersze w szczególności mam kolumnę z tekstem wejściowym.
http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/
Udało mi się to rozwiązać w następujący sposób
import { InputTextComponent } from './InputTextComponent'; //import my inputTextComponent ... var trElementList = (function (list, tableComponent) { var trList = [], trElement = undefined, trElementCreator = trElementCreator, employeeElement = undefined; // iterating through employee list and // creating row for each employee for (var x = 0; x < list.length; x++) { employeeElement = list[x]; var trNomeImpatto = React.createElement('tr', null, <td rowSpan="4"><strong>{employeeElement['NomeTipologiaImpatto'].toUpperCase()}</strong></td>); trList.push(trNomeImpatto); trList.push(trElementCreator(employeeElement, 0, x)); trList.push(trElementCreator(employeeElement, 1, x)); trList.push(trElementCreator(employeeElement, 2, x)); } // end of for return trList; // returns row list function trElementCreator(obj, field, index) { var tdList = [], tdElement = undefined; //my input text var inputTextarea = <InputTextComponent idImpatto={obj['TipologiaImpattoId']}//index value={obj[columns[field].nota]}//initial value of the input I read from my json data source noteType={columns[field].nota} impattiComposite={tableComponent.state.impattiComposite} //updateImpactCompositeNote={tableComponent.updateImpactCompositeNote} /> tdElement = React.createElement('td', { style: null }, inputTextarea); tdList.push(tdElement); var trComponent = createClass({ render: function () { return React.createElement('tr', null, tdList); } }); return React.createElement(trComponent); } // end of trElementCreator }); ... //my tableComponent var tableComponent = createClass({ // initial component states will be here // initialize values getInitialState: function () { return { impattiComposite: [], serviceId: window.sessionStorage.getItem('serviceId'), serviceName: window.sessionStorage.getItem('serviceName'), form_data: [], successCreation: null, }; }, //read a json data soure of the web api url componentDidMount: function () { this.serverRequest = $.ajax({ url: Url, type: 'GET', contentType: 'application/json', data: JSON.stringify({ id: this.state.serviceId }), cache: false, success: function (response) { this.setState({ impattiComposite: response.data }); }.bind(this), error: function (xhr, resp, text) { // show error to console console.error('Error', xhr, resp, text) alert(xhr, resp, text); } }); }, render: function () { ... React.createElement('table', {style:null}, React.createElement('tbody', null,trElementList(this.state.impattiComposite, this),)) ... } //my input text var inputTextarea = <InputTextComponent idImpatto={obj['TipologiaImpattoId']}//index value={obj[columns[field].nota]}//initial value of the input I read //from my json data source noteType={columns[field].nota} impattiComposite={tableComponent.state.impattiComposite}//impattiComposite = my json data source />//end my input text tdElement = React.createElement('td', { style: null }, inputTextarea); tdList.push(tdElement);//add a component //./InputTextComponent.js import React from 'react'; export class InputTextComponent extends React.Component { constructor(props) { super(props); this.state = { idImpatto: props.idImpatto, value: props.value, noteType: props.noteType, _impattiComposite: props.impattiComposite, }; this.updateNote = this.updateNote.bind(this); } //Update a inpute text with new value insert of the user updateNote(event) { this.setState({ value: event.target.value });//update a state of the local componet inputText var impattiComposite = this.state._impattiComposite; var index = this.state.idImpatto - 1; var impatto = impattiComposite[index]; impatto[this.state.noteType] = event.target.value; this.setState({ _impattiComposite: impattiComposite });//update of the state of the father component (tableComponet) } render() { return ( <input className="Form-input" type='text' value={this.state.value} onChange={this.updateNote}> </input> ); } }
źródło
Mój problem polegał na tym, że nazwałem kluczem dynamicznie, podając wartość elementu, w moim przypadku „nazwa”, więc kluczem był klucz = {
${item.name}-${index}
}. Więc kiedy chciałem zmienić dane wejściowe z item.name jako wartością, klucz również by się zmienił i dlatego reagował nie rozpoznałby tego elementuźródło
Okazuje się, że byłam wiążąca
this
z komponentem, który powodował, że się wycofywał.Pomyślałem, że opublikuję to tutaj na wypadek, gdyby ktoś inny miał ten problem.
Musiałem się zmienić
<Field label="Post Content" name="text" component={this.renderField.bind(this)} />
Do
<Field label="Post Content" name="text" component={this.renderField} />
Prosty dylemat, ponieważ w moim przypadku, ja faktycznie nie trzeba
this
wrenderField
, ale mam nadzieję, że mi to pomoże delegowania kogoś innego.źródło
Miałem ten problem i okazało się, że używałem komponentu funkcjonalnego i łączyłem się ze stanem komponentu nadrzędnego. Jeśli przełączyłem się na używanie komponentu klasowego, problem zniknął. Miejmy nadzieję, że jest sposób na obejście tego podczas używania funkcjonalnych komponentów, ponieważ jest to o wiele wygodniejsze dla prostych renderów elementów i innych.
źródło
zawarł następny kod w tagu wejściowym:
ref={(input) => { if (input) { input.focus(); } }}
Przed:
<input defaultValue={email} className="form-control" type="email" id="email" name="email" placeholder={"[email protected]"} maxLength="15" onChange={(e) => validEmail(e.target.value)} />
Po:
<input ref={(input) => { if (input) { input.focus(); } }} defaultValue={email} className="form-control" type="email" id="email" name="email" placeholder={"[email protected]"} maxLength="15" onChange={(e) => validEmail(e.target.value)} />
źródło