首页 文章

React js onClick无法将值传递给方法

提问于
浏览
398

我想阅读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 事件?

22 回答

  • 1

    我想,在React的 Map 中 .bind(this, arg1, arg2, ...) 是不好的代码,因为它很慢!单渲染方法中 .bind(this) 的10-50 - 代码非常慢 .
    我解决这个问题:
    渲染方法
    <tbody onClick={this.handleClickRow}>
    <tr data-id={listItem.id}> 的 Map
    处理器
    var id = $(ev.target).closest('tr').data().id

    完整代码如下:

    class MyGrid extends React.Component {
      // In real, I get this from props setted by connect in Redux
      state = {
        list: [
          {id:1, name:'Mike'},
          {id:2, name:'Bob'},
          {id:3, name:'Fred'}
        ],
        selectedItemId: 3
      }
      
      render () {
        const { list, selectedItemId }  = this.state
        const { handleClickRow } = this
    
        return (
          <table>
            <tbody onClick={handleClickRow}>
              {list.map(listItem =>(
                <tr key={listItem.id} data-id={listItem.id} className={selectedItemId===listItem.id ? 'selected' : ''}>
                  <td>{listItem.id}</td>
                  <td>{listItem.name}</td>
                </tr>
              ))}
            </tbody>
          </table>
        )
      }
      
      handleClickRow = (ev) => {
        const target = ev.target
        // You can use what you want to find TR with ID, I use jQuery
        const dataset = $(target).closest('tr').data()
        if (!dataset || !dataset.id) return
        this.setState({selectedItemId:+dataset.id})
        alert(dataset.id) // Have fun!
      }
    }
    
    ReactDOM.render(<MyGrid/>, document.getElementById("react-dom"))
    
    table {
      border-collapse: collapse;
    }
    
    .selected td {
      background-color: rgba(0, 0, 255, 0.3);
    }
    
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    
    <div id="react-dom"></div>
    
  • 2

    这里有很好的答案,我同意@Austin Greco(第二个选项有单独的组件),但我很惊讶没有人提到currying .
    你可以做的是创建一个接受参数(你的参数)的函数,并返回另一个接受另一个参数的函数(在这种情况下是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)}
                  />
                  

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

    请注意,此方法不能解决在每个渲染上创建新实例的问题 .
    我喜欢这种方法优于其他内联处理程序,因为在我看来,这个方法更简洁,更易读 .

    Edit:
    如下面的注释所示,您可以缓存/记忆函数的结果 .

    这是一个天真的实现:

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

    </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" />
    
  • 1

    [[h / t @ E.Sundin for linking this in a comment]

    最佳答案(匿名函数或绑定)将起作用,但它不是最高效的,因为它为 map() 函数生成的每个实例创建事件处理程序的副本 .

    这是对ESLint-plugin-react中最佳方法的解释:

    项目列表渲染中的绑定的常见用例是在呈现列表时,每个列表项具有单独的回调:

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

    这将加速渲染,因为它避免了在每个渲染上创建新函数(通过绑定调用)的需要 .

  • -19

    如果您使用 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>
         );
       }
     }
    
  • 859

    有三种方法可以解决这个问题: -

    • 将构造函数中的方法绑定为: -
    export class HeaderRows extends Component {
       constructor() {
           super();
           this.handleSort = this.handleSort.bind(this);
       }
    }
    
    • 使用箭头功能创建时: -
    handleSort = () => {
        // some text here
    }
    
    • 第三种方式是这样的: -
    <th value={column} onClick={() => that.handleSort} >{column}</th>
    
  • 3

    我已经通过两种方式将onclick事件值传递的代码添加到方法中 . 1 . 使用bind方法2.使用箭头(=>)方法 . 请参阅方法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>);
        }
    });
    
  • 1

    下面是在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>
             );
        }
    }
    
  • -2

    我想你必须将方法绑定到React的类实例 . 使用构造函数绑定React中的所有方法更安全 . 在您将参数传递给方法的情况下,第一个参数用于绑定方法的“this”上下文,因此您无法访问方法内的值 .

  • 67

    Theres是一个非常简单的方法 .

    onClick={this.toggleStart('xyz')} . 
      toggleStart= (data) => (e) =>{
         console.log('value is'+data);  
     }
    
  • 1
    class extends React.Component {
        onClickDiv = (column) => {
            // do stuff
        }
        render() {
            return <div onClick={() => this.onClickDiv('123')} />
        }
    }
    
  • 12

    交替尝试回答OP的问题,包括e.preventDefault()调用:

    渲染链接( ES6

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

    组件功能

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

    这是我的方法,不确定它有多糟糕,请评论

    在可点击元素中

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

    然后

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

    我编写了一个可以为此目的重用的包装器组件,它基于此处接受的答案 . 如果您需要做的只是传递一个字符串,那么只需添加一个数据属性并从e.target.dataset中读取它(就像其他人建议的那样) . 默认情况下,我的包装器将绑定到任何作为函数的prop并以'on'开头,并在所有其他事件参数之后自动将数据prop传递回调用者 . 虽然我没有测试它的性能,但它会让你有机会避免自己创建类,它可以像这样使用:

    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;
        }, {});
    }
    
  • 61

    我在JSX onClick事件上有以下3个建议 -

    • 实际上,我们不需要在代码中使用.bind()或Arrow函数 . 您可以在代码中使用它 .

    • 您还可以将onClick事件从th(或ul)移至tr(或li)以提高性能 . 基本上,你的n li元素将有n个“事件监听器” .

    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"));
    }
    
    • 我同意上面提到的为ListItem和List创建单独的React组件的方法 . 这使代码看起来不错,但是如果你有1000个li,那么将创建1000个事件监听器 . 请确保您不应该有太多的事件监听器 .
    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

    简单方法

    使用箭头功能:

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

    这将创建一个新函数,使用正确的参数调用 handleSort .

    更好的方式

    Extract it into a sub-component.在渲染调用中使用箭头函数的问题是它每次都会创建一个新函数,最终导致不需要的重新渲染 .

    如果你创建一个子组件,你可以传递处理程序并使用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}
      />
    ))}
    

    Old Easy Way (ES5)

    使用.bind传递所需的参数:

    return (
      <th value={column} onClick={that.handleSort.bind(that, column)}>{column}</th>
    );
    
  • 2

    使用箭头功能:

    您必须安装stage-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;
    
  • 2

    在这种情况下,我会建议curry over bind . 喜欢,

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

    如今,有了ES6,我觉得我们可以使用更新的答案 .

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

    基本上,(对于任何不知道的),因为 onClick 期望传递给它的函数, bind 可以工作,因为它创建了一个函数的副本 . 相反,我们可以传递一个只调用我们想要的函数的箭头函数表达式,并保留 this . 你永远不需要在React中绑定 render 方法,但如果由于某种原因你在一个组件方法中丢失 this

    constructor(props) {
      super(props);
      this.myMethod = this.myMethod.bind(this);
    }
    
  • 1

    不涉及 .bind 或ES6的另一个选项是使用带有处理程序的子组件来使用必要的道具调用父处理程序 . 这是一个例子(以下是工作示例的链接):

    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 ),允许您在父的 onClick 函数中使用任何 event 值或其他道具 .

    这是一个显示此方法的CodePen demo .

  • 15

    通过将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方法的reactjs中传递参数 .

  • 89
    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

    这个例子可能与你的有点不同 . 但我可以向您保证,这是您可以解决此问题的最佳解决方案 . 我已经搜索了几天没有性能问题的解决方案 . 最后想出了这一个 .

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

相关问题