React.js: identyfikowanie różnych danych wejściowych za pomocą jednej procedury obsługi onChange

142

Ciekawe, jaki jest właściwy sposób podejścia do tego:

var Hello = React.createClass({
getInitialState: function() {
    return {total: 0, input1:0, input2:0};
},
render: function() {
    return (
        <div>{this.state.total}<br/>
            <input type="text" value={this.state.input1} onChange={this.handleChange} />
            <input type="text" value={this.state.input2} onChange={this.handleChange} />
        </div>
    );
},
handleChange: function(e){
    this.setState({ ??? : e.target.value});
    t = this.state.input1 + this.state.input2;
    this.setState({total: t});
}
});

React.renderComponent(<Hello />, document.getElementById('content'));

Oczywiście można utworzyć oddzielne funkcje handleChange do obsługi każdego innego wejścia, ale to niezbyt przyjemne. Podobnie możesz utworzyć komponent tylko dla indywidualnego wejścia, ale chciałem sprawdzić, czy istnieje sposób, aby to zrobić w ten sposób.

T3db0t
źródło

Odpowiedzi:

163

Proponuję trzymać się standardowych atrybutów HTML, takich jak namew inputElements, aby zidentyfikować dane wejściowe. Nie musisz też utrzymywać „total” jako osobnej wartości w stanie, ponieważ można ją komponować przez dodanie innych wartości w stanie:

var Hello = React.createClass({
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },
    render: function() {
        const total = this.state.input1 + this.state.input2;

        return (
            <div>{total}<br/>
                <input type="text" value={this.state.input1} name="input1" onChange={this.handleChange} />
                <input type="text" value={this.state.input2} name="input2" onChange={this.handleChange} />
            </div>
        );
    },
    handleChange: function(e) {
        this.setState({[e.target.name]: e.target.value});
    }
});

React.renderComponent(<Hello />, document.getElementById('content'));
Ross Allen
źródło
3
Próbowałem to zrobić na początku, ale setState ({e.target.name ... nie działa - to błąd składniowy. Musisz utworzyć obiekt tymczasowy z obj [e.target.name] i ustawić na to setState. Ten rodzaj działa, ale wygląda na to, że aktualizacja obiektu stanu jest opóźniona.
T3db0t
1
"Rodzaj opóźnienia" jest ustawiony na aktualizację stanu podczas requestAnimationFrame (zakładam), więc zignoruj ​​tę uwagę
T3db0t
24
@ T3db0t Działa następująca składnia ES6:this.setState({ [e.target.name]: e.target.value });
XåpplI'-I0llwlg'I -
2
Korzystanie z bind jest lepszym podejściem. To podejście nie działa, jeśli dołączysz procedurę obsługi onChange do elementu bez właściwości name, takiego jak div.
ericgrosse,
3
W tym przypadku bind @ericgrosse nie jest lepszy, ponieważ tworzysz wiele niepotrzebnych funkcji. możesz też użyć atrybutu data- lub czegoś w innym rodzaju elementu, jeśli zajdzie taka potrzeba
aw04
106

Możesz użyć tej .bindmetody, aby wstępnie skompilować parametry handleChangemetody. To byłoby coś takiego:

  var Hello = React.createClass({
    getInitialState: function() {
        return {input1:0, 
                input2:0};
    },
    render: function() {
      var total = this.state.input1 + this.state.input2;
      return (
        <div>{total}<br/>
          <input type="text" value={this.state.input1} 
                             onChange={this.handleChange.bind(this, 'input1')} />
          <input type="text" value={this.state.input2} 
                             onChange={this.handleChange.bind(this, 'input2')} />
        </div>
      );
    },
    handleChange: function (name, e) {
      var change = {};
      change[name] = e.target.value;
      this.setState(change);
    }
  });

  React.renderComponent(<Hello />, document.getElementById('content'));

(Wykonałem również totalobliczenia w czasie renderowania, ponieważ jest to zalecane).

fiatjaf
źródło
13
Tylko krótkie przypomnienie, które this.handleChange.bind(...)tworzy nową funkcję. W przypadku, gdy używasz shouldComponentUpdatez PureRenderMixinlub coś podobnego będzie przerwa. Wszystkie komponenty wejściowe zostaną ponownie wyrenderowane po zmianie jednej wartości.
zemirco
5
Jeśli zmienisz implementację handleChangena handleChange(name) { return event => this.setState({name: event.target.value}); }, możesz przekazać "tę samą" funkcję (nie utworzysz nowej funkcji, jak jest to zrobione przy użyciu .bind()To, możesz przekazać taką funkcję:this.handleChange("input1")
Andreyco
1
Myślę, że ważne jest również, aby wspomnieć, że natura setState polega na tym, że nie będzie można pobrać bieżącego stanu w funkcji handChange, ale poprzedni stan przy użyciu na przykład this.state.input1. Właściwym podejściem będzie utworzenie wywołania zwrotnego, takiego jak this.setState(change, function () { console.log(this.state.input1); );reference: stackoverflow.com/questions/30782948/…
Charlie-Greenman
39

onChangeWydarzenie bąbelki ... Więc można zrobić coś takiego:

// A sample form
render () {
  <form onChange={setField}>
    <input name="input1" />
    <input name="input2" />
  </form>
}

Twoja metoda setField może wyglądać następująco (zakładając, że używasz ES2015 lub nowszego:

setField (e) {
  this.setState({[e.target.name]: e.target.value})
}

Używam czegoś podobnego do tego w kilku aplikacjach i jest to całkiem przydatne.

Christophera Daviesa
źródło
11

Przestarzałe rozwiązanie

valueLink/checkedLinkprzestarzałe z Core React, ponieważ dezorientują niektórych użytkowników. Ta odpowiedź nie zadziała, jeśli używasz najnowszej wersji React. Ale jeśli ci się spodoba, możesz łatwo emulować go, tworząc własny Inputkomponent

Treść starej odpowiedzi:

To, co chcesz osiągnąć, można znacznie łatwiej osiągnąć za pomocą pomocników 2-drożnego wiązania danych React.

var Hello = React.createClass({
    mixins: [React.addons.LinkedStateMixin],
    getInitialState: function() {
        return {input1: 0, input2: 0};
    },

    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
            <div>{total}<br/>
                <input type="text" valueLink={this.linkState('input1')} />;
                <input type="text" valueLink={this.linkState('input2')} />;
            </div>
        );
    }

});

React.renderComponent(<Hello />, document.getElementById('content'));

Łatwe, prawda?

http://facebook.github.io/react/docs/two-way-binding-helpers.html

Możesz nawet zaimplementować swój własny mixin

Sebastien Lorber
źródło
Dlaczego musimy wiązać zmianę, aby ustawić stan. W końcu to ReactJS!
Junaid Qadir
Zwróć uwagę, że to rozwiązanie jest przestarzałe
Filip
6

Możesz to również zrobić w ten sposób:

...
constructor() {
    super();
    this.state = { input1: 0, input2: 0 };
    this.handleChange = this.handleChange.bind(this);
}

handleChange(input, value) {
    this.setState({
        [input]: value
    })
}

render() {
    const total = this.state.input1 + this.state.input2;
    return (
        <div>
            {total}<br />
            <input type="text" onChange={e => this.handleChange('input1', e.target.value)} />
            <input type="text" onChange={e => this.handleChange('input2', e.target.value)} />
        </div>
    )
}
inostia
źródło
nie zadziałało dla mnie. zobaczyłem narzędzia programistyczne React. jego tworzenie i przypisywanie wartości do zmiennej / obiektu o nazwie input, zamiast input1 / input2
Gaurravs
1
@Gaurravs tak, masz rację. Musiałem dodać []około inputdo setstate. To sprawia, że ​​nieruchomość jest przypisywana dynamicznie
inostia
4

Można użyć specjalnego Reactatrybut o nazwie ref, a następnie dopasować węzły prawdziwy DOM w onChangeprzypadku korzystania React„s getDOMNode()funkcję:

handleClick: function(event) {
  if (event.target === this.refs.prev.getDOMNode()) {
    ...
  }
}

render: function() {
  ...
  <button ref="prev" onClick={this.handleClick}>Previous question</button>
  <button ref="next" onClick={this.handleClick}>Next question</button>
  ...
}
Janusz Kacalak
źródło
Przynajmniej w 0.13 this.refs nie ma właściwości pref.
blub
2
@usagidon - prevto tylko nazwa, którą render()
nadałem
2
Od React v0.14 możesz po prostu to zrobić event.target === this.refs.prev. facebook.github.io/react/blog/2015/10/07/…
XåpplI'-I0llwlg'I -
0

@Vigril Disgr4ce

Jeśli chodzi o formularze wielopolowe, sensowne jest użycie kluczowej cechy Reacta: komponentów.

W moich projektach tworzę komponenty TextField, które przyjmują co najmniej wartość właściwości i zajmują się obsługą typowych zachowań wejściowego pola tekstowego. W ten sposób nie musisz martwić się o śledzenie nazw pól podczas aktualizowania stanu wartości.

[...]

handleChange: function(event) {
  this.setState({value: event.target.value});
},
render: function() {
  var value = this.state.value;
  return <input type="text" value={value} onChange={this.handleChange} />;
}

[...]
manu3569
źródło
0

Możesz śledzić wartość każdego dziecka input, tworząc oddzielny InputFieldkomponent, który zarządza wartością pojedynczego input. Na przykład InputFieldmoże to być:

var InputField = React.createClass({
  getInitialState: function () {
    return({text: this.props.text})
  },
  onChangeHandler: function (event) {
     this.setState({text: event.target.value})
  }, 
  render: function () {
    return (<input onChange={this.onChangeHandler} value={this.state.text} />)
  }
})

Teraz każda wartość inputmoże być śledzone w oddzielnej instancji tego InputFieldkomponentu bez tworzenia odrębnych wartości w stanie rodzica monitorować każdy element podrzędny.

Javasamurai
źródło
0

Zapewnię naprawdę proste rozwiązanie problemu. Załóżmy, że mamy dwa wejścia usernamei password, ale chcemy, aby nasz uchwyt był łatwy i ogólny, abyśmy mogli go ponownie wykorzystać i nie pisać kodu standardowego.

I nasza forma:

                <form>
                    <input type="text" name = "username" onChange={this.onChange} value={this.state.username}/>
                    <input type="text" name = "password" onChange={this.onChange} value={this.state.password}/>
                    <br></br>
                    <button type="submit">Submit</button>
                </form>

II. Nasz konstruktor, którego chcemy zapisać usernamei passwordabyśmy mieli do nich łatwy dostęp:

constructor(props) {
    super(props);
    this.state = {
        username: '',
        password: ''
    };

    this.onSubmit = this.onSubmit.bind(this);
    this.onChange = this.onChange.bind(this);
}

III. Interesujące i „ogólne” podejście z tylko jednym onChangezdarzeniem opiera się na tym:

onChange(event) {
    let inputName = event.target.name;
    let value = event.target.value;

    this.setState({[inputName]:value});


    event.preventDefault();
}

Pozwól mi wyjaśnić:

1. Po wykryciu zmiany onChange(event)wywoływana jest funkcja

Następnie otrzymujemy parametr nazwy pola i jego wartość:

let inputName = event.target.name; ex: username

let value = event.target.value; ex: itsgosho

Na podstawie parametru name pobieramy naszą wartość ze stanu w konstruktorze i aktualizujemy ją o wartość:

this.state['username'] = 'itsgosho'

4. Kluczową kwestią jest to, że nazwa pola musi być zgodna z naszym parametrem w stanie

Mam nadzieję, że komuś jakoś pomogłem :)

Georgi Peev
źródło
0

Klucz twojego stanu powinien być taki sam jak nazwa twojego pola wejściowego. Następnie możesz to zrobić w metodzie handleEvent;

this.setState({
        [event.target.name]: event.target.value
});
Rajan
źródło
-1

Cześć, poprawiłem ssorallen odpowiedź. Nie musisz wiązać funkcji, ponieważ możesz uzyskać dostęp do wejścia bez niej.

var Hello = React.createClass({
    render: function() {
        var total = this.state.input1 + this.state.input2;
        return (
             <div>{total}<br/>
                  <input type="text" 
                    value={this.state.input1}
                    id="input1"  
                    onChange={this.handleChange} />
                 <input type="text" 
                    value={this.state.input2}
                    id="input2" 
                    onChange={this.handleChange} />
            </div>
       );
   },
   handleChange: function (name, value) {
       var change = {};
       change[name] = value;
       this.setState(change);
   }
});

React.renderComponent(<Hello />, document.getElementById('content'));
Albert Olivé
źródło