javascript - props - react state




React js onClick無法將值傳遞給方法 (18)

簡單的方法

使用箭頭功能:

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

這將創建一個新函數,該函數使用正確的參數調用 handleSort

更好的方法

將其提取到子組件中。 在render調用中使用箭頭函數的問題是,每次都會創建一個新函數,最終導致不必要的重新渲染。

如果創建子組件,則可以傳遞處理程序並將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>
);

我想閱讀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 事件?


[[/ h到@ E.Sundin,以在評論中 鏈接此內容 ]

最佳答案(匿名函數或綁定)將起作用,但它並不是最有效的方法,因為它為 map() 函數生成的每個實例創建事件處理程序的副本。

這是從 ESLint-plugin-react中 執行此操作的最佳方法的說明:

項目清單

渲染中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>
    );

});

這將加速渲染,因為它避免了在每個渲染上創建新函數(通過綁定調用)的需要。


不涉及 .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演示, 展示了此方法的實際作用。


使用箭頭功能:

您必須安裝階段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; 

如下所述,通過將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中工作。


如今,有了ES6,我覺得我們可以使用更新的答案。

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

基本上,(對於任何不知道的人)由於 onClick 期望將函數傳遞給它,因此 bind 可以工作,因為它創建了函數的副本。 相反,我們可以傳遞一個箭頭函數表達式,該表達式僅調用所需的函數並保留 this 。 您永遠不需要在React中綁定 render 方法,但是如果由於某種原因而在一個組件方法中丟失了 this

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

您可以這樣使用代碼:

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

如果要在句柄函數中使用諸如 preventDefault() 類的事件方法,或者要獲取目標值或名稱(如 e.target.name preventDefault() ,則e用於事件對象。


我以兩種方式為onclick事件值傳遞的代碼添加了代碼。 1。 使用bind方法2.使用arrow(=>)方法。 查看方法handlesort1和handlesort

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

我意識到這對於聚會來說還很晚,但是我認為更簡單的解決方案可以滿足許多用例:

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

    ...

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

我想您也可以使用 data- attribute。

簡單,語義。


我猜您將不得不將方法綁定到React的類實例。 使用構造函數綁定React中的所有方法比較安全。 在您將參數傳遞給方法的情況下,第一個參數用於綁定方法的“ this”上下文,因此您無法訪問方法內部的值。


我編寫了一個包裝器組件,該組件可在此基於已接受答案的基礎上重複使用。 但是,如果您只需要傳遞一個字符串,則只需添加一個數據屬性,然後從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;
    }, {});
}

我認為React的地圖中的 .bind(this, arg1, arg2, ...) 是不好的代碼,因為它很慢! 單一渲染方法中的 .bind(this).bind(this) 非常慢的代碼。
我這樣修復:
渲染方法
<tbody onClick={this.handleClickRow}>
<tr data-id={listItem.id}> 映射
處理程序
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> 


有一個非常簡單的方法。

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

此示例可能與您的示例稍有不同。 但我可以向您保證,這是解決此問題的最佳解決方案。 我已經搜索了幾天,沒有性能問題的解決方案。 最後想出了這個。

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

更新

如果您要處理應該作為參數的對象。 您可以使用 JSON.stringify(object) 將其轉換為字符串並添加到數據集中。

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

這是我的方法,不確定是否有問題,請發表評論

在可點擊元素中

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

接著

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

這裡有很好的答案,我同意@Austin Greco(第二個選項,有單獨的組件)
我喜歡另一種方式, currying
您可以做的是創建一個接受參數(您的參數)的函數並返回另一個接受另一個參數的函數(在這種情況下為click事件)。 那麼您就可以隨心所欲地使用它。

ES5:

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

ES6:

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

您將以這種方式使用它:

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

    };
}

這是此類用法的完整示例:

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>


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

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




reactjs