web-dev-qa-db-ja.com

反応js onClickはメソッドに値を渡ることができません

OnClickイベント値のプロパティを読みたいです。しかし、それをクリックすると、コンソールに次のようなものが表示されます。

SyntheticMouseEvent {dispatchConfig: Object, dispatchMarker: ".1.1.0.2.0.0:1", nativeEvent: MouseEvent, type: "click", target

私のコードは正しく働いています。実行すると{column}は表示されますがonClickイベントで取得できません。

私のコード:

var HeaderRows = React.createClass({
    handleSort:  function(value) {
       console.log(value);
    },
    render: function () {
        var that = this;
        return(
        <tr>
            {this.props.defaultColumns.map(function (column) {
                return (
                    <th value={column} onClick={that.handleSort} >{column}</th>
                );
            })}
            {this.props.externalColumns.map(function (column) {
                // Multi dimension array - 0 is column name
                var externalColumnName = column[0];
                return ( <th>{externalColumnName}</th>
                );
            })}

        </tr>);
    }
});

React jsのonClickイベントに値を渡すにはどうすればいいですか?

487
user1924375

簡単な方法

矢印機能を使う:

return (
  <th value={column} onClick={() => this.handleSort(column)}>{column}</th>
);

これは正しいパラメータでhandleSortを呼び出す新しい関数を作成します。

もっと良い方法

それをサブコンポーネントに展開します。 レンダーコールでarrow関数を使うことの問題は毎回新しい関数を作成することになります。不必要な再レンダリングを引き起こします。

サブコンポーネントを作成する場合は、handlerを渡して引数としてpropsを使用できます。これにより、propsが変更された場合にのみ再レンダリングされます(ハンドラ参照は変更されないため)。

サブコンポーネント

class TableHeader extends Component {
  handleClick = () => {
    this.props.onHeaderClick(this.props.value);
  }

  render() {
    return (
      <th onClick={this.handleClick}>
        {this.props.column}
      </th>
    );
  }
}

主成分

{this.props.defaultColumns.map((column) => (
  <TableHeader
    value={column}
    onHeaderClick={this.handleSort}
  />
))}

オールドイージーウェイ(ES5)

必要なパラメータを渡すには、 .bind を使用します。

return (
  <th value={column} onClick={that.handleSort.bind(that, column)}>{column}</th>
);
1026
Austin Greco

今日、ES6では、最新の回答を使用できると思います。

return (
  <th value={column} onClick={()=>this.handleSort(column)} >{column}</th>
);

基本的に、(知らない人のために)onClickは渡された関数を期待しているので、bindは関数のコピーを作成するので機能します。代わりに、欲しい関数を呼び出してthisを保存する矢印関数式を渡すことができます。 Reactでrenderメソッドをバインドする必要はありませんが、何らかの理由でコンポーネントメソッドの1つでthisを失う場合は、

constructor(props) {
  super(props);
  this.myMethod = this.myMethod.bind(this);
}
108
aikeru

ここにいい答えがあります、そして私は@Austin Grecoに同意します(別々のコンポーネントを持つ2番目のオプション)
私が好むもう一つの方法は、 カレー です。
できることは、パラメータを受け取る関数(自分のパラメータ)を作成し、別のパラメータ(この場合はclickイベント)を受け取る別の関数を返すことです。それならあなたは今まであなたが望んでいたことを自由に行うことができます。

ES5:

handleChange(param) { // param is the argument you passed to the function
    return function (e) { // e is the event object that returned

    };
}

ES6:

handleChange = param => e => {
    // param is the argument you passed to the function
    // e is the event object that returned
};

そして、あなたはこのようにそれを使うでしょう:

<input 
    type="text" 
    onChange={this.handleChange(someParam)} 
/>

これがそのような使い方の完全な例です:

const someArr = ["A", "B", "C", "D"];

class App extends React.Component {
  state = {
    valueA: "",
    valueB: "some initial value",
    valueC: "",
    valueD: "blah blah"
  };

  handleChange = param => e => {
    const nextValue = e.target.value;
    this.setState({ ["value" + param]: nextValue });
  };

  render() {
    return (
      <div>
        {someArr.map(obj => {
          return (
            <div>
              <label>
                {`input ${obj}   `}
              </label>
              <input
                type="text"
                value={this.state["value" + obj]}
                onChange={this.handleChange(obj)}
              />
              <br />
              <br />
            </div>
          );
        })}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

この方法では、各レンダリングでの新しいインスタンスの作成は解決されません。
私の意見では、これはより簡潔で読みやすいので、他のインラインハンドラーよりもこのアプローチが好きです。

編集:
以下のコメントで示唆されているように、関数の結果をキャッシュ/記憶することができます。

これは単純な実装です。

let memo = {};

const someArr = ["A", "B", "C", "D"];

class App extends React.Component {
  state = {
    valueA: "",
    valueB: "some initial value",
    valueC: "",
    valueD: "blah blah"
  };

  handleChange = param => {
    const handler = e => {
      const nextValue = e.target.value;
      this.setState({ ["value" + param]: nextValue });
    }
    if (!memo[param]) {
      memo[param] = e => handler(e)
    }
    return memo[param]
  };

  render() {
    return (
      <div>
        {someArr.map(obj => {
          return (
            <div key={obj}>
              <label>
                {`input ${obj}   `}
              </label>
              <input
                type="text"
                value={this.state["value" + obj]}
                onChange={this.handleChange(obj)}
              />
              <br />
              <br />
            </div>
          );
        })}
      </div>
    );
  }
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root" />
99
Sagiv b.g

[[h/tから@ E.Sundinへ コメント内のthis へのリンク]

一番上の答え(匿名関数または束縛)は機能しますが、map()関数によって生成されたすべてのインスタンスに対してイベントハンドラーのコピーを作成するので、それは最も高性能なものではありません。

これは ESLint-plugin-react から実行するのに最適な方法の説明です。

商品リスト

Renderにおけるbindの一般的なユースケースは、リストをレンダリングするとき、リストアイテムごとに別々のコールバックを持つことです。

const List = props => (
      <ul>
        {props.items.map(item =>
          <li key={item.id} onClick={() => console.log(item.id)}>
            ...
          </li>
        )}
      </ul>
    );

このようにするのではなく、繰り返されるセクションを独自のコンポーネントにプルします。

const List = props => (
      <ul>
        {props.items.map(item =>
          <ListItem 
            key={item.id} 
            item={item} 
            onItemClick={props.onItemClick} // assume this is passed down to List
           />
        )}
      </ul>
    );


const ListItem = props => {
  const _onClick = () => {
    console.log(props.item.id);
  }
    return (
      <li onClick={_onClick}>
        ...
      </li>
    );

});

これにより、レンダリングごとに(バインド呼び出しを通じて)新しい関数を作成する必要がなくなるため、レンダリングが高速化されます。

64
Brandon

これは私のアプローチです、それがどれほど悪いかわからない、コメントしてください

クリック可能な要素内

return (
    <th value={column} onClick={that.handleSort} data-column={column}>   {column}</th>
);

その後

handleSort(e){
    this.sortOn(e.currentTarget.getAttribute('data-column'));
}
20

この例はあなたのものとは少し違うかもしれません。しかし、私はこれがあなたがこの問題に対して持つことができる最善の解決策であることをあなたに保証することができます。パフォーマンスの問題がない解決策を何日もかけて探しました。そしてついにこれを思いついた。

class HtmlComponent extends React.Component {
  constructor() {
    super();
    this.state={
       name:'MrRehman',
    };
    this.handleClick= this.handleClick.bind(this);
  }

  handleClick(event) {
    const { param } = e.target.dataset;
    console.log(param);
    //do what you want to do with the parameter
  }

  render() {
    return (
      <div>
        <h3 data-param="value what you wanted to pass" onClick={this.handleClick}>
          {this.state.name}
        </h3>
      </div>
    );
  }
}

UPDATE

パラメータとなるはずのオブジェクトを扱いたい場合。 JSON.stringify(object)を使って文字列に変換し、データセットに追加することができます。

return (
          <div>
            <h3 data-param={JSON.stringify({name:'me'})} onClick={this.handleClick}>
              {this.state.name}
            </h3>
          </div>
        );
14
hannad rehman

.bindまたはES6を含まないもう1つのオプションは、必要な小道具を使って親ハンドラーを呼び出すためにハンドラーを持つ子コンポーネントを使用することです。これが例です(そして、実例へのリンクは以下です):

var HeaderRows = React.createClass({
  handleSort:  function(value) {
     console.log(value);
  },
  render: function () {
      var that = this;
      return(
          <tr>
              {this.props.defaultColumns.map(function (column) {
                  return (
                      <TableHeader value={column} onClick={that.handleSort} >
                        {column}
                      </TableHeader>
                  );
              })}
              {this.props.externalColumns.map(function (column) {
                  // Multi dimension array - 0 is column name
                  var externalColumnName = column[0];
                  return ( <th>{externalColumnName}</th>
                  );
              })}
          </tr>);
      )
  }
});

// A child component to pass the props back to the parent handler
var TableHeader = React.createClass({
  propTypes: {
    value: React.PropTypes.string,
    onClick: React.PropTypes.func
  },
  render: function () {
    return (
      <th value={this.props.value} onClick={this._handleClick}
        {this.props.children}
      </th>
    )        
  },
  _handleClick: function () {
    if (this.props.onClick) {
      this.props.onClick(this.props.value);
    }
  }
});

基本的な考え方は、親コンポーネントがonClick関数を子コンポーネントに渡すことです。子コンポーネントはonClick関数を呼び出し、それに渡された任意のprops(およびevent)にアクセスできます。これにより、親のevent関数内で任意のonClick値または他の小道具を使用できます。

これが CodePenデモ です。

4
Brett DeWoody
class extends React.Component {
    onClickDiv = (column) => {
        // do stuff
    }
    render() {
        return <div onClick={() => this.onClickDiv('123')} />
    }
}

私はこれが当事者にはかなり遅いと認識していますが、もっと簡単な解決策が多くのユースケースを満たすことができると思います。

    handleEdit(event) {
        let value = event.target.value;
    }

    ...

    <button
        value={post.id}
        onClick={this.handleEdit} >Edit</button>

data-属性も使えると思います。

シンプル、意味的.

3
jhchnc

以下で説明するように、メインコンポーネントからサブコンポーネントへのパラメータとしてcountを渡すことで、オブジェクトからのshow total countを実装します。

これがMainComponent.jsです。

import React, { Component } from "react";

import SubComp from "./subcomponent";

class App extends Component {

  getTotalCount = (count) => {
    this.setState({
      total: this.state.total + count
    })
  };

  state = {
    total: 0
  };

  render() {
    const someData = [
      { name: "one", count: 200 },
      { name: "two", count: 100 },
      { name: "three", count: 50 }
    ];
    return (
      <div className="App">
        {someData.map((nameAndCount, i) => {
          return (
            <SubComp
              getTotal={this.getTotalCount}
              name={nameAndCount.name}
              count={nameAndCount.count}
              key={i}
            />
          );
        })}
        <h1>Total Count: {this.state.total}</h1>
      </div>
    );
  }
}

export default App;

そしてこれがSubComp.jsです

import React, { Component } from 'react';
export default class SubComp extends Component {

  calculateTotal = () =>{
    this.props.getTotal(this.props.count);
  }

  render() {
    return (
      <div>
        <p onClick={this.calculateTotal}> Name: {this.props.name} || Count: {this.props.count}</p>
      </div>
    )
  }
};

上記を実装してみてください。そうすれば、どのDOMメソッドの場合でもpassパラメータがreactjsでどのように機能するかという正確なシナリオがわかります。

2
nandu

Onclickイベントの値をメソッドに渡すためのコードを2つの方法で追加しました。 1。 bindメソッドを使う2. arrow(=>)メソッドを使うhandleort1メソッドとhandleortメソッドを参照してください。

var HeaderRows  = React.createClass({
    getInitialState : function() {
      return ({
        defaultColumns : ["col1","col2","col2","col3","col4","col5" ],
        externalColumns : ["ecol1","ecol2","ecol2","ecol3","ecol4","ecol5" ],

      })
    },
    handleSort:  function(column,that) {
       console.log(column);
       alert(""+JSON.stringify(column));
    },
    handleSort1:  function(column) {
       console.log(column);
       alert(""+JSON.stringify(column));
    },
    render: function () {
        var that = this;
        return(
        <div>
            <div>Using bind method</div>
            {this.state.defaultColumns.map(function (column) {
                return (
                    <div value={column} style={{height : '40' }}onClick={that.handleSort.bind(that,column)} >{column}</div>
                );
            })}
            <div>Using Arrow method</div>

            {this.state.defaultColumns.map(function (column) {
                return (
                    <div value={column} style={{height : 40}} onClick={() => that.handleSort1(column)} >{column}</div>

                );
            })}
            {this.state.externalColumns.map(function (column) {
                // Multi dimension array - 0 is column name
                var externalColumnName = column;
                return (<div><span>{externalColumnName}</span></div>
                );
            })}

        </div>);
    }
});
2

私はこの目的のために再利用できるラッパーコンポーネントを書きました。ただし、文字列を渡すだけでよい場合は、data-attributeを追加し、それをe.target.datasetから読み取るだけです(他の人が提案したように)。デフォルトでは、私のラッパーは関数であるすべてのプロップにバインドし、 'on'で始まり、他のすべてのイベント引数の後に自動的にデータプロップを呼び出し元に返します。パフォーマンスについてはテストしていませんが、自分でクラスを作成しないようにすることができます。このように使用できます。

const DataButton = withData('button')

const DataInput = withData('input');

またはコンポーネントと機能

const DataInput = withData(SomeComponent);

またはあなたが好めば

const DataButton = withData(<button/>)

あなたのコンテナの外側(輸入の近く)にそれを宣言する

これがコンテナでの使い方です。

import withData from './withData';
const DataInput = withData('input');

export default class Container extends Component {
    state = {
         data: [
             // ...
         ]
    }

    handleItemChange = (e, data) => {
        // here the data is available 
        // ....
    }

    render () {
        return (
            <div>
                {
                    this.state.data.map((item, index) => (
                        <div key={index}>
                            <DataInput data={item} onChange={this.handleItemChange} value={item.value}/>
                        </div>
                    ))
                }
            </div>
        );
    }
}

これがラッパーコード 'withData.jsです。

import React, { Component } from 'react';

const defaultOptions = {
    events: undefined,
}

export default (Target, options) => {
    Target = React.isValidElement(Target) ? Target.type : Target;
    options = { ...defaultOptions, ...options }

    class WithData extends Component {
        constructor(props, context){
            super(props, context);
            this.handlers = getHandlers(options.events, this);        
        }

        render() {
            const { data, children, ...props } = this.props;
            return <Target {...props} {...this.handlers} >{children}</Target>;
        }

        static displayName = `withData(${Target.displayName || Target.name || 'Component'})`
    }

    return WithData;
}

function getHandlers(events, thisContext) {
    if(!events)
        events = Object.keys(thisContext.props).filter(prop => prop.startsWith('on') && typeof thisContext.props[prop] === 'function')
    else if (typeof events === 'string')
        events = [events];

    return events.reduce((result, eventType) => {
        result[eventType] = (...args) => thisContext.props[eventType](...args, thisContext.props.data);
        return result;
    }, {});
}
2
SlimSim

これはとても簡単な方法です。

 onClick={this.toggleStart('xyz')} . 
  toggleStart= (data) => (e) =>{
     console.log('value is'+data);  
 }
1
Roma Mukherjee

E.preventDefault()呼び出しを含む、OPの質問に答えるための別の試みをする:

レンダリングされたリンク(ES6

<a href="#link" onClick={(e) => this.handleSort(e, 'myParam')}>

コンポーネント機能

handleSort = (e, param) => {
  e.preventDefault();
  console.log('Sorting by: ' + param)
}
1
Po Rith

私はJSX onClickイベントでこれについて3つ以下の提案をしています -

  1. 実際には、コード内で.bind()やArrow関数を使用する必要はありません。あなたのコードで簡単に使用することができます。

  2. パフォーマンスを向上させるために、onClickイベントをth(またはul)からtr(またはli)に移動することもできます。基本的に、n li要素にはn個の "Event Listeners"があります。

    So finally code will look like this:
    <ul onClick={this.onItemClick}>
        {this.props.items.map(item =>
               <li key={item.id} data-itemid={item.id}>
                   ...
               </li>
          )}
    </ul>
    

    //そして、以下に示すようにonItemClickメソッドでitem.idにアクセスできます。

    onItemClick = (event) => {
       console.log(e.target.getAttribute("item.id"));
    }
    
  3. ListItemとListに対して別々のReactコンポーネントを作成するための上記のアプローチに同意します。ただし、このmakeコードは、1000個のliがある場合は1000個のEvent Listenersが作成されます。たくさんのイベントリスナーを持たないようにしてください。

    import React from "react";
    import ListItem from "./ListItem";
    export default class List extends React.Component {
    
        /**
        * This List react component is generic component which take props as list of items and also provide onlick
        * callback name handleItemClick
        * @param {String} item - item object passed to caller
        */
        handleItemClick = (item) => {
            if (this.props.onItemClick) {
                this.props.onItemClick(item);
            }
        }
    
        /**
        * render method will take list of items as a props and include ListItem component
        * @returns {string} - return the list of items
        */
        render() {
            return (
                <div>
                  {this.props.items.map(item =>
                      <ListItem key={item.id} item={item} onItemClick={this.handleItemClick}/>
                  )}
                </div>
            );
        }
    
    }
    
    
    import React from "react";
    
    export default class ListItem extends React.Component {
        /**
        * This List react component is generic component which take props as item and also provide onlick
        * callback name handleItemClick
        * @param {String} item - item object passed to caller
        */
        handleItemClick = () => {
            if (this.props.item && this.props.onItemClick) {
                this.props.onItemClick(this.props.item);
            }
        }
        /**
        * render method will take item as a props and print in li
        * @returns {string} - return the list of items
        */
        render() {
            return (
                <li key={this.props.item.id} onClick={this.handleItemClick}>{this.props.item.text}</li>
            );
        }
    }
    
1
Reetesh Agrawal

以下はonClickイベントに値を渡す例です。

私はes6構文を使いました。クラスコンポーネントの中で覚えておいてください矢印関数は自動的にバインドされないので、コンストラクタで明示的にバインドします。

class HeaderRows extends React.Component {

    constructor(props) {
        super(props);
        this.handleSort = this.handleSort.bind(this);
    }

    handleSort(value) {
        console.log(value);
    }

    render() {
        return(
            <tr>
                {this.props.defaultColumns.map( (column, index) =>
                    <th value={ column } 
                        key={ index } 
                        onClick={ () => this.handleSort(event.target.value) }>
                        { column }
                    </th>
                )}

                {this.props.externalColumns.map((column, index)  =>
                    <th value ={ column[0] }
                        key={ index }>
                        {column[0]}
                    </th>
                )}
            </tr>
         );
    }
}
1
karan singh

矢印機能を使う:

ステージ2をインストールする必要があります。

npm install babel-preset-stage-2をインストールします。

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value=0
        }
    }

    changeValue = (data) => (e) => {
        alert(data);  //10
        this.setState({ [value]: data })
    }

    render() {
        const data = 10;
        return (
            <div>
                <input type="button" onClick={this.changeValue(data)} />
            </div>
        );
    }
}
export default App; 
1
sm chinna
class TableHeader extends Component {
  handleClick = (parameter,event) => {
console.log(parameter)
console.log(event)

  }

  render() {
    return (
      <button type="button" 
onClick={this.handleClick.bind(this,"dataOne")}>Send</button>
    );
  }
}
1
Anik Mazumder

あなたがES6を使っているならば、あなたはそれを単にすることができます。

export default class Container extends Component {
  state = {
    data: [
        // ...
    ]
  }

  handleItemChange = (e, data) => {
      // here the data is available 
      // ....
  }
  render () {
     return (
        <div>
        {
           this.state.data.map((item, index) => (
              <div key={index}>
                  <Input onChange={(event) => this.handItemChange(event, 
                         item)} value={item.value}/>
              </div>
           ))
        }
        </div>
     );
   }
 }
1
Louis Alonzo
1. You just have to use an arrow function in the Onclick event like this: 

<th value={column} onClick={() => that.handleSort(theValue)} >{column}</th>

2.Then bind this in the constructor method:
    this.handleSort = this.handleSort.bind(this);

3.And finally get the value in the function:
  handleSort(theValue){
     console.log(theValue);
}
1
Juan David Arce

メソッドをReactのクラスインスタンスにバインドする必要があると思います。 Reactのすべてのメソッドをバインドするためにコンストラクタを使用する方が安全です。あなたのケースでは、メソッドにパラメータを渡すとき、最初のパラメータはメソッドの 'this'コンテキストをバインドするために使われるので、メソッド内の値にアクセスすることはできません。

1
Andysenclave

あなたはこのようにあなたのコードを使うことができます:

<th value={column} onClick={(e) => that.handleSort(e, column)} >{column}</th>

ハンドル関数でpreventDefault()のようなイベントメソッドを使用したい場合、またはe.target.nameのようなターゲット値または名前を取得したい場合は、eはイベントオブジェクト用です。

0
Bankim Sutaria

これを処理する3つの方法があります: -

  1. 次のようにコンストラクタ内でメソッドをバインドします -

    export class HeaderRows extends Component {
       constructor() {
           super();
           this.handleSort = this.handleSort.bind(this);
       }
    }
    
  2. としてそれを作成しながら矢印関数を使用します -

    handleSort = () => {
        // some text here
    }
    
  3. 第三の方法はこれです: -

    <th value={column} onClick={() => that.handleSort} >{column}</th>
    
0
Mansi Teharia

私は、タグの属性としてparamを渡すという解決策が非常に合理的であることを見つけました。

しかしそれには限界があります。

  • リスト項目に他のタグがあると正しく動作しません(したがって、event.targetは意図したものと異なる場合があります)。
  • パラメータは文字列のみです。シリアル化と逆シリアル化が必要です。

私がこのライブラリを思いついたのはこのためです。 https://github.com/sneas/react-event-param

それ:

  • 必要に応じて親で必要な属性を検索することにより、子供の問題を解決します
  • Paramを自動的にシリアライズおよびデシリアライズする
  • 設定と取得のロジックをカプセル化します。パラメータ名をいじる必要はありません

使用例

import { setEventParam, getEventParam } from "react-event-param";

class List extends Component {
  onItemClick = e => {
    const index = getEventParam(e.target);
    // Do something with index
  };

  render() {
    return (
      <ul>
        {this.props.items.map((itemText, index) => (
          <li
            key={index}
            {...setEventParam(index)}
            onClick={this.onItemClick}
          >
            {{ itemText }}
          </li>
        ))}
      </ul>
    );
  }
}

export default List;
0
sneas