React Funkcja onClick uruchamia się podczas renderowania

212

Przekazuję 2 wartości do elementu potomnego:

  1. Lista obiektów do wyświetlenia
  2. funkcja usuwania.

Używam funkcji .map () do wyświetlania mojej listy obiektów (jak w przykładzie podanym na stronie z instrukcją reagowania), ale przycisk w tym komponencie uruchamia onClickfunkcję podczas renderowania (nie powinien odpalać w czasie renderowania). Mój kod wygląda następująco:

module.exports = React.createClass({
    render: function(){
        var taskNodes = this.props.todoTasks.map(function(todo){
            return (
                <div>
                    {todo.task}
                    <button type="submit" onClick={this.props.removeTaskFunction(todo)}>Submit</button>
                </div>
            );
        }, this);
        return (
            <div className="todo-task-list">
                {taskNodes}
            </div>
        );
    }
});

Moje pytanie brzmi: dlaczego onClickfunkcja odpala podczas renderowania i jak go nie robić?

Stralos
źródło

Odpowiedzi:

528

Ponieważ wywołujesz tę funkcję zamiast przekazywania funkcji do onClick, zmień tę linię na:

<button type="submit" onClick={() => { this.props.removeTaskFunction(todo) }}>Submit</button>

=> o nazwie Arrow Function, która została wprowadzona w ES6 i będzie obsługiwana w React 0.13.3 lub wyższej.

Długi Nguyen
źródło
1
jak to zrobić w coffescript?
vipin8169
14
Możesz także unikać nawiasów klamrowych z funkcją strzałki. Które, moim zdaniem, pasują do najlepszych praktyk:onClick={() => this.props.removeTaskFn(todo)}
sospedra
1
Czy mógłbyś wyjaśnić to trochę więcej? Rozumiem, że ludzie powtarzają, że nie jest to najlepsza praktyka, ale chciałbym zrozumieć, co się tutaj dzieje dokładnie z () => Rozumiem, co to jest funkcja strzałki, ale nie to, co to jest () i dlaczego to jest złe?
wuno,
@ wuno () to parametry twojej anonimowej funkcji. Tutaj jest pusto, ponieważ nie przekazujemy żadnych parametrów. Wyobraź sobie, że () jest () funkcji (). W związku z tym, dlaczego nie jest najlepszą praktyką wiązania w funkcji render (), jest to, że na każdym renderowaniu ponownie przypisujemy funkcję do komponentu, co może być bardzo kosztowne.
jaysonder
@LongNguyen Tego właśnie szukam! wielkie dzięki
M. Wiśnicki
31

Zamiast wywoływać funkcję, powiąż wartość z funkcją:

this.props.removeTaskFunction.bind(this, todo)

MDN ref: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

Pete TNT
źródło
2
IMHO i wiele innych. Powinieneś faworyzować funkcje bezpaństwowe nad klauzulami lub wiązaniami, ponieważ mogą one prowadzić do niepożądanych efektów ubocznych. Dlatego, nawet jeśli obie odpowiedzi są poprawne, uważam, że jedna jest bardziej godna podziwu niż druga.
sospedra
1
Bezpośrednie wiązanie w renderowaniu lub w dowolnym innym miejscu komponentu nie jest zalecane. Wiązanie powinno się zawsze odbywać w konstruktorze
Hemadri Dasari
15

Wartość tego onClickatrybutu powinna być funkcją, a nie wywołaniem funkcji.

<button type="submit" onClick={function(){removeTaskFunction(todo)}}>Submit</button>
Brian Swisher
źródło
8
Nie używaj anonimowych funkcji do wywołań zdarzeń wewnątrz renderowania - spowoduje to uruchomienie kolejnego renderowania
Splynx
4

Dla tych, którzy nie używają funkcji strzałek, ale coś prostszego ... Zetknąłem się z tym podczas dodawania nawiasów po funkcji signOut ...

wymień to <a onClick={props.signOut()}>Log Out</a>

z tym <a onClick={props.signOut}>Log Out</a>...! 😆

olisteadman
źródło
Właściwie to w ogóle mi nie działa. Przekazałem tę funkcję i nigdy nie dodałem nawiasu, który wciąż odpalał się podczas renderowania. Nie jestem pewien, czy zmieniło się to od lutego 1919 roku, ale przypisanie funkcji jak w odpowiedzi Long Nguyen i Vishala Bishta rozwiązało problem.
BitShift
4

JSX jest używany z ReactJS, ponieważ jest bardzo podobny do HTML i daje programistom poczucie używania HTML, podczas gdy ostatecznie transponuje się do pliku javascript.

Zapisanie pętli for i określenie funkcji jako {this.props.removeTaskFunction (todo)} spowoduje wykonanie funkcji za każdym razem, gdy zostanie uruchomiona pętla.

Aby zatrzymać to zachowanie, musimy przywrócić funkcję onClick.

Funkcja grubej strzałki ma ukrytą instrukcję return wraz z właściwością bind . W ten sposób zwraca funkcję OnClick, ponieważ JavaScript może również zwrócić funkcje !!!!!

Posługiwać się -

onClick={() => { this.props.removeTaskFunction(todo) }}

co znaczy-

var onClick = function() {
  return this.props.removeTaskFunction(todo);
}.bind(this);
Vishal Bisht
źródło
1

JSX oceni wyrażenia JavaScript w nawiasach klamrowych

W takim przypadku this.props.removeTaskFunction(todo)wywoływana jest wartość przypisana do zwracanej wartościonClick

Musisz podać onClickfunkcję. Aby to zrobić, możesz zawinąć wartość w anonimową funkcję.

export const samepleComponent = ({todoTasks, removeTaskFunction}) => {
    const taskNodes = todoTasks.map(todo => (
                <div>
                    {todo.task}
                    <button type="submit" onClick={() => removeTaskFunction(todo)}>Submit</button>
                </div>
            );
    return (
        <div className="todo-task-list">
            {taskNodes}
        </div>
        );
    }
});
Sudo Bangbang
źródło
1

Miałem podobny problem, mój kod to:

function RadioInput(props) {
    return (
    <div className="form-check form-check-inline">
        <input className="form-check-input" type="radio" name="inlineRadioOptions" id={props.id} onClick={props.onClick} value={props.label}></input>
        <label className="form-check-label" htmlFor={props.id}>{props.label}</label>
    </div>
    );
  }
class ScheduleType extends React.Component
{
    renderRadioInput(id,label)
    {
        id = "inlineRadio"+id;
        return(
            <RadioInput
                id = {id}
                label = {label}
                onClick = {this.props.onClick}
            />
        );

    }

Gdzie powinno być

onClick = {() => this.props.onClick()}

w RenderRadioInput

Naprawiono problem dla mnie.

Seyhak Ly
źródło