React.js - yeniden oluştururken odağı kaybeden girdi


105

Sadece metin girişine yazıyorum ve onChangearadığımda setStateReact kullanıcı arayüzümü yeniden oluşturuyor. Sorun, metin girişinin her zaman odağı kaybetmesidir, bu nedenle her harf için yeniden odaklanmam gerekir: 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>
        );
    }

});

Bunun olmasının tek nedeni, a) başka bir şeyin odağı çalması veya b) girdinin titremesidir. Sorunu gösteren bir jsfiddle / jsbin sağlayabilir misiniz? İşte temel tepki jsbin .
Brigand

1
lol ... kulağa biraz can sıkıcı geliyor: P jquery ile yeni oluşturulan girdi için bir tanımlayıcı ayarlardım ve sonra odağı çağırırdım. Düz js'de kodun nasıl görüneceğinden emin değilim. Ama eminim yapabilirsin :)
Medda86

1
@FakeRainBrigand: Titreyerek ne demek istiyorsun?
Krab

@Krab, like if this.props.selected yanlış oluyordu ve sonra tekrar gerçek oluyordu. Bu, girişin ayrılmasına ve ardından tekrar bağlanmasına neden olur.
Brigand

@Krab, slimScroll satırlarını kaldırmayı deneyin; tuhaf bir şey yapmak sorunlara neden olabilir.
Brigand

Yanıtlar:


91

Kodunuzun geri kalanını görmeden bu bir tahmin. Bir EditorContainer oluşturduğunuzda, bileşen için benzersiz bir anahtar belirtin:

<EditorContainer key="editor1"/>

Bir yeniden oluşturma gerçekleştiğinde, aynı anahtar görülürse, bu, React'e görünümü bozma ve görünümü yeniden oluşturma, bunun yerine yeniden kullanma söyleyecektir. Daha sonra odaklanan öğe odağı korumalıdır.


2
Bu, girdi formu içeren bir alt bileşeni oluşturma sorunumu çözdü. Ama benim durumumda tam tersine ihtiyacım vardı - form istediğimde yeniden oluşturulmuyordu. Anahtar özelliğini eklemek işe yaradı.
Sam Texas

2
girdiye anahtar eklemek yardımcı olmadı
Mïchael Makaröv

6
Belki girişinizi içeren bileşenler de yeniden oluşturulmuştur. Çevreleyen bileşenlerdeki anahtarları kontrol edin.
Petr Gladkikh

@PetrGladkikh tarafından yapılan yoruma olumlu oy verin. Tüm kapalı bileşenlerde anahtarlara ihtiyacım vardı.
Brian Cragun'ın

58

Buraya tekrar tekrar geliyorum ve her zaman sonunda başka yerlerime çözüm buluyorum. Bu yüzden burada belgeleyeceğim çünkü bunu tekrar unutacağımı biliyorum!

inputDurumumdaki odak noktasını kaybetmemin nedeni , inputdurum değişikliğini yeniden gerçekleştiriyor olmamdı .

Buggy Kodu:

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

Yani sorun şu ki, hızlıca test etmek ve daha sonra hepsini ayrı modüllere ayırmak için her zaman her şeyi tek bir yerde kodlamaya başlıyorum. Ancak, burada bu strateji geri teper çünkü girdi değişikliğindeki durumun güncellenmesi işlevi işletir ve odak kaybolur.

Düzeltmek basittir, modülerleştirmeyi baştan yapın, başka bir deyişle, " InputBileşeni renderişlev dışı bırakın"

Sabit Kod

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

Ref. çözüme: https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947


1
Oluşturma işlevi içinde bir HOC kullanıyordum, HOC çağrısını bileşen tanımına taşımak hile yaptı. Bir şekilde bu yanıta benziyor.
Mark E

3
Sanırım bu benim sorunum, ancak Bileşenleri referans nedeniyle dışına render()ve dışına taşımada sorun yaşıyorum . örneğinclass ... extends ComponentthisonChange={this.handleInputChange}
Nateous

4
Hikayenin morali, React sınıfı bileşenleri için, renderişlev içindeki bileşenleri tanımlamaz , React işlevsel bileşenleri için, işlev gövdesindeki bileşenleri tanımlamaz.
Wong Jia Hau

1
Teşekkürler! Kıçımı kurtardın !!
K-Sato

1
@Nateous Ben de aynı durumdaydım. HOC render, 'this.handleChange' yönteminden geçirilen işlevin içini çağırır ve kullanılacak bileşenleri döndürür. Örn const TextAreaDataField = TextAreaDataFieldFactory(this.handleChange). Basitçe bileşen oluşturma işlemini kurucuya taşıdım ve renderyöntemde onlara eriştim this.TextAreaDataField. Bir cazibe gibi çalıştı.
Marcus Junius Brutus

36

Bir yönlendirici tepki içinde bir sorun varsa <Route/>kullanmak renderyerine pervane component.

<Route path="/user" render={() => <UserPage/>} />


Odak kaybı, componentpervane React.createElementher seferinde değişiklikleri yeniden oluşturmak yerine kullandığı için olur .

Ayrıntılar burada: https://reacttraining.com/react-router/web/api/Route/component


2
Bu esasen benim sorunumdu. Özellikle, anonim bir işlevi bileşen pervanesine iletiyordum ve odak kaybını tetikliyordum:, <Route component={() => <MyComponent/>} />ne zaman yapmalıydım<Route component={MyComponent} />
Daniel

Günümü kurtardın - bu kadar basit!
Fabricio

12

Cevabım @ z5h'nin söylediğine benzer.

Benim durumumda, bileşen için Math.random()bir benzersiz oluşturuyordum key.

keyBu dizideki tüm bileşenleri yeniden oluşturmak yerine yalnızca söz konusu bileşen için bir yeniden oluşturucuyu tetiklemek için kullanıldığını düşündüm ( kodumda bir dizi bileşen döndürürüm). Yeniden oluşturduktan sonra durumu geri yüklemek için kullanıldığını bilmiyordum.

Bunu kaldırmak benim için iş yaptı.


1
Npmjs.com/package/shortid gibi bir modül de böyle bir şeyi halletmenin güzel bir yoludur.
skwidbreth

5

autoFocusÖzniteliğin inputöğeye uygulanması, odaklanılması gereken yalnızca bir girdinin olduğu durumlarda geçici bir çözüm olarak kullanılabilir. Bu durumda, bir keyöznitelik gereksiz olacaktır çünkü bu sadece bir unsurdur ve ayrıca input, ana bileşenin yeniden oluşturulmasına odaklanmayı kaybetmemek için öğeyi kendi bileşenine ayırma konusunda endişelenmenize gerek kalmaz .


Bunu son çare olarak denemek zorundaydım. Çalıştığına sevindim. Sayfadaki tek arama girişini yazmaya çalıştığımda uygulama yeniden oluşturulduğu için uygulamamda açıkça bir hata var. Ancak autofocusgiriş alanına ulaşmak bir başlangıçtır. 🙌
keshavDulal

5

Ben de aynı davranışlarım var.

Kodumdaki sorun, bunun gibi iç içe geçmiş bir jsx öğeleri dizisi oluşturmamdı:

const example = [
            [
                <input value={'Test 1'}/>,
                <div>Test 2</div>,
                <div>Test 3</div>,
            ]
        ]

...

render = () => {
    return <div>{ example }</div>
}

Bu iç içe dizideki her öğe, ana öğeyi her güncellediğimde yeniden işliyor. Ve böylece girişler her seferinde orada "ref" prop kaybeder

İç diziyi bir tepki bileşenine (render işlevli bir işlev) dönüştürerek Sorunu düzelttim

const example = [
            <myComponentArray />
        ]

 ...

render = () => {
    return <div>{ example }</div>
}

DÜZENLE:

Aynı sorun yuvalanmış bir React.Fragment

const SomeComponent = (props) => (
    <React.Fragment>
        <label ... />
        <input ... />
    </React.Fragment>
);

const ParentComponent = (props) => (
    <React.Fragment>
        <SomeComponent ... />
        <div />
    </React.Fragment>
);

3

Girişteki ve ana öğelerindeki anahtar niteliği silerek aynı sorunu çözdüm

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


2

Bu sorunla karşılaştım ve yardım için buraya geldim. CSS'nizi kontrol edin! Giriş alanı user-select: none;bir iPad'de olamaz veya çalışmaz.


2

Benim için bu, arama sonuçları listesiyle aynı bileşende (Kullanıcı Listesi olarak adlandırılır) oluşturulan arama giriş kutusundan kaynaklanıyordu . Bu nedenle, arama sonuçları her değiştiğinde, giriş kutusu dahil olmak üzere tüm UserList bileşeni yeniden oluşturulur.

Benim çözümüm, UserList'ten ayrı olan UserListSearch adlı yepyeni bir bileşen oluşturmaktı . Bunun çalışması için UserListSearch'teki giriş alanlarında anahtar ayarlamama gerek yoktu. UsersContainer'ımın render işlevi artık şöyle görünüyor:

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

Umarım bu da birine yardımcı olur.


2

Girdi alanı başka bir elemanın içindeyse (yani <div key={"bart"}...><input key={"lisa"}...> ... </input></div>- burada atlanan kodu gösteren elipsler gibi bir kap elemanı ), kap elemanında (ve aynı zamanda girdi alanında) benzersiz ve sabit bir anahtar olmalıdır . Aksi takdirde, React, yalnızca eski kabı yeniden oluşturmak yerine çocuğun durumu güncellendiğinde yepyeni bir kapsayıcı öğesi oluşturur. Mantıksal olarak, yalnızca alt öğe güncellenmelidir, ancak ...

Bir sürü adres bilgisi alan bir bileşeni yazmaya çalışırken bu sorunu yaşadım. Çalışma kodu şuna benzer

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

Keskin okuyucular <fieldset>, bunun kapsayıcı bir öğe olduğunu, ancak bir anahtar gerektirmediğini fark edeceklerdir . Aynı şey <>ve <React.Fragment>hatta <div>Neden için de geçerlidir ? Belki sadece acil konteynerin bir anahtara ihtiyacı vardır. Bilmiyorum. Matematik ders kitaplarının dediği gibi, açıklama alıştırma olarak okuyucuya bırakılmıştır.


1

Temel neden şudur: React yeniden oluştururken, önceki DOM referansınız geçersiz olacaktır. Bu, react'in DOM ağacını değiştirdiği anlamına gelir ve siz this.refs.input.focus çalışmayacaktır, çünkü buradaki girdi artık mevcut değildir.


1

Verilen cevaplar bana yardımcı olmadı, yaptığım şey buydu ama benzersiz bir durumum vardı.

Kodu temizlemek için, bileşeni başka bir dosyaya çekmeye hazır olana kadar bu biçimi kullanma eğilimindeyim.

render(){
   const MyInput = () => {
      return <input onChange={(e)=>this.setState({text: e.target.value}) />
   }
   return(
      <div>
         <MyInput />
      </div>
   )

Ancak bu, kodu doğrudan çalıştığı div'e koyduğumda odağını kaybetmesine neden oldu.

return(
   <div>
      <input onChange={(e)=>this.setState({text: e.target.value}) />
   </div>
)

Nedenini bilmiyorum, bu şekilde yazarken yaşadığım tek sorun bu ve bunu sahip olduğum çoğu dosyada yapıyorum, ancak biri benzer bir şey yaparsa bu yüzden odak noktasını kaybediyor.


1

Ben açık valuedikmenin defaultValue. Bu benim için çalışıyor.

...
// before
<input value={myVar} />

// after
<input defaultValue={myVar} />

1

Bir sütuna girdi metin satırları olan bir html tablosunda da aynı sorunu yaşadım. Bir döngü içinde bir json nesnesi okurum ve özellikle satırlar yaratırım. Girdi metnine sahip bir sütunum var.

http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/

Şu şekilde çözmeyi başardım

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>
            );
          }
        }

1

Benim sorunum, anahtarımı dinamik olarak öğenin bir değeriyle adlandırmamdı, benim durumumda "ad" yani anahtar anahtar = { ${item.name}-${index}} idi. Bu yüzden, değer olarak item.name ile girdiyi değiştirmek istediğimde, anahtar da değişecek ve bu nedenle tepki vermesi o elemanı tanımayacaktır.


0

thisYeniden oluşturulmasına neden olan bileşene bağlandığım ortaya çıktı.

Başkasının bu sorunu yaşaması durumunda buraya göndereceğimi düşündüm.

Değiştirmek zorunda kaldım

<Field
    label="Post Content"
    name="text"
    component={this.renderField.bind(this)}
/>

İçin

<Field
    label="Post Content"
    name="text"
    component={this.renderField}
/>

Benim durumumda beri Basit düzeltme, aslında gerek yoktu thisiçinde renderField, ama umarım beni başka bu irade yardım birisini gönderme.


0

Bu sorunu yaşadım ve sorun, işlevsel bir bileşen kullanmam ve bir ana bileşenin durumu ile bağlantı kurmamdı. Bir sınıf bileşeni kullanmaya geçersem sorun ortadan kalktı. Basit öğe oluşturucular ve diğerleri için çok daha uygun olduğundan, işlevsel bileşenleri kullanırken bunun etrafından dolaşmanın bir yolu vardır.


0

sonraki kodu etiket girişine dahil etti:

ref={(input) => {
     if (input) {
         input.focus();
     }
 }}

Önce:

<input
      defaultValue={email}
      className="form-control"
      type="email"
      id="email"
      name="email"
      placeholder={"mail@mail.com"}
      maxLength="15"
      onChange={(e) => validEmail(e.target.value)}
/>

Sonra:

<input
     ref={(input) => {
          if (input) {
             input.focus();
          }
      }}
      defaultValue={email}
      className="form-control"
      type="email"
      id="email"
      name="email"
      placeholder={"mail@mail.com"}
      maxLength="15"
      onChange={(e) => validEmail(e.target.value)}
/>

benim durumum için çalıştı.
Mr Special
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.