ตอบสนอง js onClick ไม่สามารถส่งผ่านค่าไปยังวิธีการ


638

ฉันต้องการอ่านคุณสมบัติค่าเหตุการณ์ 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>
    );
  }
});

ฉันจะส่งผ่านค่าไปยังonClickกิจกรรมใน React js ได้อย่างไร


2
มีความเป็นไปได้ที่ซ้ำกันของการผูกเหตุการณ์ OnClick ใน React.js
WiredPrairie

2
พิจารณาใช้ตัวเองแทนสิ่งนั้น นั่นคือความเข้าใจผิดอย่างเป็นธรรมตามที่ควรจะตรงกันกับ "นี้" (ไม่ได้สิ่งที่สำคัญจริงๆแม้ว่าแต่ละของเขาเอง)
แดน

การใช้วิธีผูกและวิธีลูกศรเราสามารถส่งผ่านค่าไปยังเหตุการณ์ Onclick
Merugu Prashanth

คำตอบ:


1285

ทางที่ง่าย

ใช้ฟังก์ชั่นลูกศร:

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

สิ่งนี้จะสร้างฟังก์ชั่นใหม่ที่เรียกhandleSortใช้พารามิเตอร์ที่ถูกต้อง

ทางที่ดีกว่า

แยกมันออกเป็นองค์ประกอบย่อย ปัญหาเกี่ยวกับการใช้ฟังก์ชั่นลูกศรในการเรียกใช้การเรนเดอร์คือมันจะสร้างฟังก์ชั่นใหม่ทุกครั้งซึ่งจะทำให้เกิดการแสดงผลซ้ำโดยไม่จำเป็น

หากคุณสร้างส่วนประกอบย่อยคุณสามารถส่งตัวจัดการและใช้อุปกรณ์ประกอบฉากเป็นอาร์กิวเมนต์ซึ่งจะสร้างการแสดงผลก็ต่อเมื่ออุปกรณ์ประกอบฉากเปลี่ยนแปลง (เพราะตอนนี้การอ้างอิงผู้จัดการไม่เคยเปลี่ยนแปลง):

องค์ประกอบย่อย

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

7
ตอบสนองให้คำเตือนเมื่อฉันใช้เช่นรหัสของคุณ ฉันเปลี่ยนรหัสของฉันเป็น onClick = {that.onClick.bind (null, คอลัมน์)}
user1924375

12
คุณจะใช้มันอย่างไรกับ<a>แท็กที่คุณต้องการส่งเหตุการณ์เพื่อใช้งานpreventDefault()
Simon H

38
@SimonH bindเหตุการณ์จะผ่านเป็นอาร์กิวเมนต์สุดท้ายหลังจากการขัดแย้งคุณผ่านทาง
เลอะเทอะ

47
สิ่งนี้ไม่เลวสำหรับประสิทธิภาพหรือไม่ ฟังก์ชั่นใหม่จะถูกสร้างขึ้นในการแสดงผลแต่ละครั้งหรือไม่?
AndrewMcLagan

13
@AndrewMcLagan มันเป็น ฉันพบสิ่งนี้เพื่ออธิบายกฎและวิธีแก้ปัญหาที่มีประสิทธิภาพมากที่สุด
E. Sundin

140

มีคำตอบที่ดีอยู่ที่นี่และผมเห็นด้วยกับ @Austin กรีก (ตัวเลือกที่สองที่มีส่วนประกอบที่แยกต่างหาก)
ไม่มีทางที่ผมชอบก็คือcurrying
สิ่งที่คุณสามารถทำได้คือการสร้างฟังก์ชั่นที่ยอมรับพารามิเตอร์ (พารามิเตอร์ของคุณ) และส่งกลับฟังก์ชั่นอื่นที่ยอมรับพารามิเตอร์อื่น (เหตุการณ์คลิกในกรณีนี้) จากนั้นคุณมีอิสระที่จะทำในสิ่งที่คุณต้องการ

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

นี่คือตัวอย่างการใช้งานดังกล่าว:

โปรดทราบว่าวิธีการนี้ไม่ได้แก้ปัญหาการสร้างอินสแตนซ์ใหม่ในแต่ละการแสดงผล
ฉันชอบวิธีนี้มากกว่าตัวจัดการแบบอินไลน์อื่น ๆ เนื่องจากวิธีนี้มีความกระชับและอ่านง่ายกว่าในความคิดของฉัน

แก้ไข:
ตามที่แนะนำในความคิดเห็นด้านล่างคุณสามารถแคช / บันทึกผลลัพธ์ของฟังก์ชันได้

นี่คือการดำเนินการที่ไร้เดียงสา:


12
นี่ควรเป็นคำตอบที่ยอมรับได้ ง่ายต่อการติดตั้งใช้งานจริงและคุณไม่จำเป็นต้องสร้างส่วนประกอบอื่น ๆ หรือผูกมัดต่างกัน ขอบคุณ!
Tamb

29
ดูดีขึ้น แต่จากมุมมองของประสิทธิภาพการปิดตาไม่ช่วยจริงๆเพราะถ้าคุณเรียก handleChange สองครั้งด้วย param เดียวกันคุณจะได้รับสองฟังก์ชันที่เอ็นจิน JS พิจารณาว่าเป็นวัตถุแยกกันแม้ว่าพวกเขาจะทำสิ่งเดียวกัน . ดังนั้นคุณยังคงได้รับการแสดงผลอีกครั้ง เพื่อประสิทธิภาพการทำงานคุณจะต้องแคชผลลัพธ์ของ handleChange เพื่อรับประโยชน์จากประสิทธิภาพการทำงาน ชอบhandleChange = param => cache[param] || (e => { // the function body })
travellingprog

6
นี่เป็นคำตอบที่สมบูรณ์แบบถ้าคุณทำตามคำแนะนำจาก @travellingprog
llioor

2
ใครสามารถให้ลิงค์หรืออธิบายว่ากลไกการแคชนี้ทำงานอย่างไร ขอบคุณ
Sadok Mtir

2
สวยงามและเรียบร้อย!
Hatem Alimam

117

ทุกวันนี้ด้วย ES6 ฉันรู้สึกว่าเราสามารถใช้คำตอบที่อัปเดตแล้วได้

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

โดยทั่วไป (สำหรับสิ่งที่ไม่ทราบ) เนื่องจากonClickคาดว่าจะมีฟังก์ชั่นที่ส่งไปยังมันbindทำงานได้เพราะมันสร้างสำเนาของฟังก์ชั่น thisแต่เราสามารถผ่านฟังก์ชั่นการแสดงออกลูกศรที่เพียงแค่เรียกใช้ฟังก์ชั่นที่เราต้องการและรักษา คุณไม่จำเป็นต้องผูกrenderเมธอดใน React แต่ถ้าด้วยเหตุผลบางอย่างที่คุณทำหายไปกับthisหนึ่งในวิธีส่วนประกอบของคุณ:

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

10
คุณไม่จำเป็นต้องผูกrender()เพราะมันถูกเรียกโดยปฏิกิริยา หากมีสิ่งใดคุณต้องbindมีตัวจัดการเหตุการณ์และเฉพาะเมื่อคุณไม่ได้ใช้ลูกศร
Dan Abramov

1
@DanAbramov ฉันคิดว่าคุณถูกต้อง แต่ฉันรวมไว้ในกรณี - อัปเดตด้วยตัวอย่างที่ไม่สนับสนุนการเรนเดอร์ที่มีผลผูกพัน
aikeru

3
โปรดทราบว่าวิธีที่ดีที่สุดในการส่งpropsต่อsuper()หรือthis.propsจะไม่ถูกกำหนดในระหว่างตัวสร้างซึ่งอาจสร้างความสับสน
Dan Abramov

2
เพื่อให้บรรลุอะไร คุณสามารถกำหนดตัวจัดการภายในองค์ประกอบการทำงานและผ่านมัน มันจะเป็นฟังก์ชั่นที่แตกต่างกันทุกการเรนเดอร์ดังนั้นหากคุณพิจารณาด้วย profiler ว่ามันทำให้คุณสมบูรณ์แบบ (และเฉพาะในกรณีที่คุณแน่ใจในเรื่องนี้จริงๆ!) ลองใช้คลาสแทน
Dan Abramov

1
@ me-me ใช่และถ้าคุณเลือกที่จะใช้ JavaScript ใน Babel คุณควรจะประกาศคุณสมบัติในชั้นเรียนของคุณfoo = () => {...}แล้วอ้างอิงในการแสดง<button onClick={this.foo}...เหตุผลเดียวที่จะรวมฟังก์ชั่นลูกศรถ้าคุณใช้ Babel ด้วยวิธีนี้ IMO คือถ้าคุณต้องการจับตัวแปรบางอย่างที่มีอยู่ในขอบเขตวิธีการแสดงผล () เท่านั้น (เหตุการณ์สามารถส่งผ่านไปได้และไม่สามารถใช้งานได้)
aikeru

73

[[h / t เพื่อ @ E.Sundin สำหรับเชื่อมโยงสิ่งนี้ในความคิดเห็น]

คำตอบยอดนิยม (ฟังก์ชั่นที่ไม่ระบุชื่อหรือการผูก) จะทำงานได้ แต่มันก็ไม่ได้มีประสิทธิภาพมากที่สุดเพราะมันจะสร้างสำเนาของตัวจัดการเหตุการณ์สำหรับทุกอินสแตนซ์ที่สร้างโดย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>
    );

});

สิ่งนี้จะช่วยเร่งการเรนเดอร์เนื่องจากไม่ต้องสร้างฟังก์ชั่นใหม่ (ผ่านการผูกสาย) ในทุกเรนเดอร์


ตอบสนองทำให้เกิดการเรียกใช้ฟังก์ชั่นเหล่านั้นด้วยการโทร / นำไปใช้จากนั้นภายใต้ประทุนและหลีกเลี่ยงการใช้การผูกภายใน
aikeru

1
มีวิธีในการทำเช่นนี้โดยใช้องค์ประกอบไร้สัญชาติหรือไม่?
Carlos Martinez

2
@CarlosMartinez ตาดีฉันอัปเดตตัวอย่าง - พวกเขาควรจะเป็นองค์ประกอบการทำงานไร้สัญชาติ (SFC) ในตอนแรก โดยทั่วไปหากองค์ประกอบใดไม่เคยใช้งานthis.stateคุณสามารถสลับออกได้อย่างปลอดภัยด้วย SFC
Brandon

3
อืมฉันไม่เข้าใจว่ามันมีประสิทธิภาพมากกว่านี้ได้อย่างไร ฟังก์ชัน ListItem จะไม่ถูกเรียกใช้ทุกครั้งดังนั้นฟังก์ชัน _onClick จะถูกสร้างขึ้นทุกการแสดงผล
Mattias Petter Johansson

1
ฉันอยู่ไกลจากผู้เชี่ยวชาญที่นี่ แต่ตามที่ฉันเข้าใจในรูปแบบ 'ถูกต้อง' มีเพียงตัวอย่างหนึ่งของตัวจัดการและมันผ่านเสาสำหรับองค์ประกอบใดขององค์ประกอบเรียกมันว่า ในตัวอย่างการโยง (เช่นรูปแบบ 'ผิด') จะมีอินสแตนซ์หนึ่งของตัวจัดการสำหรับทุกองค์ประกอบอินสแตนซ์ เป็นประเภทของหน่วยความจำเทียบเท่าการเขียนฟังก์ชั่นเดียวกันสามสิบครั้งรองเขียนครั้งเดียวและเรียกมันในกรณีที่จำเป็น
แบรนดอน

25

นี่คือวิธีการของฉันไม่แน่ใจว่ามันแย่แค่ไหนโปรดแสดงความคิดเห็น

ในองค์ประกอบที่คลิกได้

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

แล้ว

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

นี่เป็นวิธีที่ฉันคิดว่ามันรู้สึกแฮ็คเล็กน้อย แต่หลีกเลี่ยงการสร้างองค์ประกอบใหม่ ฉันไม่แน่ใจว่า getAttribute ดีกว่าหรือแย่กว่าเมื่อเปรียบเทียบกับการดึงส่วนประกอบแยกต่างหาก
kamranicus

2
ฉันคิดว่ามันเป็นทางออกที่ดีเพราะมันง่ายมาก แต่ใช้งานได้เฉพาะกับค่าสตริงหากคุณต้องการวัตถุมันไม่ทำงาน
Stephane L

สำหรับวัตถุที่คุณจะต้องทำencodeURIComponent(JSON.stringify(myObj))แล้วต้องแยกมันJSON.parse(decodeURIComponent(myObj))ออกมา สำหรับฟังก์ชั่นฉันค่อนข้างแน่ใจว่าสิ่งนี้จะไม่ทำงานโดยไม่มี eval หรือ Function ใหม่ () ทั้งสองอย่างควรหลีกเลี่ยง ด้วยเหตุผลเหล่านี้ฉันไม่ได้ใช้ data-attributes เพื่อส่งผ่านข้อมูลใน React / JS
Solvitieg

ฉันต้องการเพิ่มฉันไม่ได้ใช้สิ่งนี้บ่อยและเฉพาะสิ่งเล็กน้อย แต่ฉันมักจะสร้างองค์ประกอบและส่งผ่านข้อมูลเป็นอุปกรณ์ประกอบฉากไปยังมัน จากนั้นจัดการคลิกภายในองค์ประกอบใหม่นั้นหรือส่งผ่านฟังก์ชั่น onClick ไปยังองค์ประกอบ ไลค์ได้อธิบายไว้ในคำตอบแบรนดอน
Santiago Ramirez

1
ชุดข้อมูลสามารถเข้าถึงได้โดยตรงด้วยวิธีนี้บนเบราว์เซอร์ที่ทันสมัย ​​(รวมถึง IE11): e.currentTarget.dataset.column
gsziszi

17

ตัวอย่างนี้อาจแตกต่างจากของคุณเล็กน้อย แต่ฉันรับรองได้ว่านี่เป็นทางออกที่ดีที่สุดสำหรับปัญหานี้ ฉันค้นหาวันเพื่อหาวิธีแก้ปัญหาที่ไม่มีปัญหาประสิทธิภาพ และในที่สุดก็มากับอันนี้

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

1
สิ่งนี้ไม่ทำงานเมื่อข้อมูลที่ส่งผ่านเป็นวัตถุ
SlimSim

ใช้ JSON.stringify เพื่อแก้ไขปัญหา @SlimSim ที่ควรทำเคล็ดลับ
hannad rehman

หากคุณต้องการใช้ JSON.stringify สำหรับปัญหานี้อาจเป็นวิธีที่ไม่ถูกต้อง กระบวนการของการทำให้เป็นสตริงใช้หน่วยความจำจำนวนมาก
KFE

ในกรณีส่วนใหญ่คุณจะส่ง ID เป็นพารามิเตอร์เท่านั้นและรับรายละเอียดวัตถุตาม ID นั้นจากวัตถุต้นทางของคุณ และทำไมและอย่างไรจึงต้องใช้หน่วยความจำมากฉันรู้ว่า JSON stringify นั้นช้า แต่ click Fn นั้นเป็นแบบอะซิงก์และจะไม่มีผลกระทบใด ๆ กับ dom หรือ 0 เมื่อสร้างแล้ว
hannad rehman

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

2
เหมือนกัน - DOM ใหม่ในแต่ละการเรนเดอร์ ดังนั้น React จะอัปเดต DOM ทุกครั้ง
Alex Shwarc

4

อีกหนึ่งตัวเลือกที่ไม่เกี่ยวข้อง.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) ช่วยให้คุณสามารถใช้eventค่าใด ๆหรืออุปกรณ์ประกอบฉากอื่น ๆ ภายในonClickฟังก์ชั่นของผู้ปกครอง

นี่คือตัวอย่างของCodePen ที่แสดงวิธีการนี้


4

พยายามสำรองเพื่อตอบคำถามของ OP รวมถึงการโทร e.preventDefault ():

ลิงก์แสดงผล ( ES6 )

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

ฟังก์ชั่นส่วนประกอบ

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

4

ฉันรู้ว่านี่มันค่อนข้างช้าไปงานปาร์ตี้ แต่ฉันคิดว่าวิธีแก้ปัญหาที่ง่ายกว่านั้นสามารถตอบสนองการใช้งานได้หลากหลาย:

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

    ...

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

ฉันคิดว่าคุณสามารถใช้data-คุณลักษณะ

ง่ายความหมาย


4

เพียงสร้างฟังก์ชั่นเช่นนี้

  function methodName(params) {
    //the thing  you wanna do
  }

และเรียกมันในสถานที่ที่คุณต้องการ

<ไอคอน onClick = {() => {methodName (theParamsYouwantToPass); }} />


3

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

3

การใช้แสดงจำนวนรวมจากวัตถุโดยผ่านการนับเป็นพารามิเตอร์จากส่วนประกอบหลักไปยังส่วนประกอบย่อยตามที่อธิบายไว้ด้านล่าง

นี่คือ 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>
    )
  }
};

ลองใช้งานด้านบนและคุณจะได้รับสถานการณ์ที่แน่นอนว่าการทำงานของพารามิเตอร์ pass ใน reactjs ใน DOM ใด ๆ


3

ฉันเขียนส่วนประกอบของ wrapper ที่สามารถนำกลับมาใช้ใหม่เพื่อจุดประสงค์นี้ซึ่งสร้างจากคำตอบที่ยอมรับได้ที่นี่ หากสิ่งที่คุณต้องทำคือส่งผ่านสตริงเพียงแค่เพิ่ม data-attribute และอ่านจาก e.target.dataset (เหมือนที่คนอื่น ๆ แนะนำ) โดยค่าเริ่มต้น wrapper ของฉันจะผูกติดกับเสาใด ๆ ที่เป็นฟังก์ชั่นและเริ่มต้นด้วย 'เปิด' และส่งเสาข้อมูลกลับไปยังผู้โทรโดยอัตโนมัติหลังจากที่มีข้อโต้แย้งเหตุการณ์อื่น ๆ ทั้งหมด แม้ว่าฉันจะไม่ได้ทดสอบประสิทธิภาพ แต่มันจะให้โอกาสคุณในการหลีกเลี่ยงการสร้างชั้นเรียนด้วยตัวเองและสามารถใช้ในลักษณะนี้:

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

นี่คือโค้ดของ wrapper '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

ฉันมีข้อเสนอแนะต่ำกว่า 3 ข้อนี้ใน JSX onClick Events -

  1. ที่จริงแล้วเราไม่จำเป็นต้องใช้ฟังก์ชั่น. ผูก () หรือลูกศรในรหัสของเรา คุณสามารถใช้งานได้ง่ายในรหัสของคุณ

  2. นอกจากนี้คุณยังสามารถย้ายเหตุการณ์ onClick จาก th (หรือ ul) ไปยัง tr (หรือ li) เพื่อปรับปรุงประสิทธิภาพ โดยทั่วไปคุณจะมีจำนวน "ผู้ฟังกิจกรรม" สำหรับองค์ประกอบ n li ของคุณ

    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>

    // และคุณสามารถเข้าถึงitem.idในonItemClickวิธีการดังต่อไปนี้:

    onItemClick = (event) => {
       console.log(e.target.getAttribute("item.id"));
    }
  3. ฉันเห็นด้วยกับวิธีการดังกล่าวข้างต้นสำหรับการสร้างชิ้นส่วน React แยกต่างหากสำหรับ ListItem และ List รหัสนี้ดูดี แต่ถ้าคุณมี 1,000 ตัวจากนั้นจะมีตัวสร้างเหตุการณ์ 1,000 ตัว โปรดตรวจสอบว่าคุณไม่ควรมีผู้ฟังเหตุการณ์มาก

    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
สิ่งนี้ไม่ทำงานเมื่อข้อมูลที่คุณต้องการผ่านเป็นวัตถุ คุณลักษณะนี้จะทำงานกับสตริงเท่านั้น การอ่านจาก dom ผ่านทาง get attribute นั้นน่าจะเป็นการดำเนินการที่แพงกว่า
SlimSim

2

ฉันได้เพิ่มรหัสสำหรับค่าเหตุการณ์ onclick ผ่านไปยังวิธีการในสองวิธี 1. ใช้วิธีการผูก 2. ใช้ลูกศร (=>) วิธีการ ดูวิธีจัดการหมายเลข 1 และหมายเลขอ้างอิง

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

ด้านล่างเป็นตัวอย่างที่ส่งผ่านค่าในเหตุการณ์ 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 ในกรณีของคุณเมื่อคุณส่งพารามิเตอร์ไปยังวิธีการพารามิเตอร์แรกจะใช้ในการผูกบริบท 'นี้' ของวิธีการดังนั้นคุณไม่สามารถเข้าถึงค่าภายในวิธีการ


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


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

4
ในขณะที่รหัสนี้อาจแก้ปัญหารวมถึงคำอธิบายช่วยปรับปรุงคุณภาพของโพสต์ของคุณ จำไว้ว่าคุณกำลังตอบคำถามสำหรับผู้อ่านในอนาคตและคนเหล่านั้นอาจไม่ทราบสาเหตุของการแนะนำรหัสของคุณ
Shree

2

ออกมาจากคำถามนี้ไม่มีที่ไหนเลย แต่ฉันคิดว่า.bindจะทำเคล็ดลับ ค้นหารหัสตัวอย่างด้านล่าง

const handleClick = (data) => {
    console.log(data)
}

<button onClick={handleClick.bind(null, { title: 'mytitle', id: '12345' })}>Login</button>

1

ใช้ฟังก์ชั่นลูกศร:

คุณต้องติดตั้ง stage-2:

npm ติดตั้ง 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; 

0

มี 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

คุณสามารถใช้รหัสของคุณเช่นนี้:

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

นี่เป็นจดหมายสำหรับวัตถุเหตุการณ์ถ้าคุณต้องการที่จะใช้วิธีการเหตุการณ์เช่นในฟังก์ชั่นการจัดการของคุณหรือต้องการที่จะได้รับค่าเป้าหมายหรือชื่อเหมือนpreventDefault()e.target.name


0

ฉันพบวิธีแก้ปัญหาของการส่งพารามิเตอร์เป็นแอตทริบิวต์ของแท็กค่อนข้างสมเหตุสมผล

อย่างไรก็ตามมันมีข้อ จำกัด :

  • ทำงานไม่ถูกต้องเมื่อรายการมีแท็กอื่น ๆ (เช่น event.target อาจแตกต่างจากที่ตั้งใจไว้)
  • Param อาจเป็นสตริงเท่านั้น ต้องเป็นอันดับและ deserialization

นั่นเป็นเหตุผลที่ฉันมากับห้องสมุดนี้: react-event-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

มีการพิจารณาประสิทธิภาพมากมายในสุญญากาศ
ปัญหาที่เกิดขึ้นกับตัวจัดการนี้คือคุณต้องแกงพวกเขาเพื่อรวมอาร์กิวเมนต์ที่คุณไม่สามารถตั้งชื่อในอุปกรณ์ประกอบฉาก
ซึ่งหมายความว่าองค์ประกอบต้องการตัวจัดการสำหรับองค์ประกอบที่คลิกได้แต่ละรายการ เราตกลงกันว่าสำหรับปุ่มสองสามปุ่มนี้ไม่ใช่ปัญหาใช่ไหม
ปัญหาเกิดขึ้นเมื่อคุณจัดการข้อมูลตารางด้วยคอลัมน์หลายสิบคอลัมน์และหลายพันแถว ที่นั่นคุณสังเกตเห็นผลกระทบของการสร้างตัวจัดการจำนวนมาก

ความจริงก็คือฉันต้องการเพียงหนึ่ง
ฉันตั้งค่าตัวจัดการที่ระดับตาราง (หรือ UL หรือ OL ... ) และเมื่อการคลิกเกิดขึ้นฉันสามารถบอกได้ว่าเซลล์ที่ถูกคลิกโดยใช้ข้อมูลที่มีอยู่ตั้งแต่ในวัตถุเหตุการณ์:

nativeEvent.target.tagName
nativeEvent.target.parentElement.tagName 
nativeEvent.target.parentElement.rowIndex
nativeEvent.target.cellIndex
nativeEvent.target.textContent

ฉันใช้ฟิลด์ tagname เพื่อตรวจสอบว่าการคลิกเกิดขึ้นในองค์ประกอบที่ถูกต้องตัวอย่างเช่นละเว้นการคลิกในส่วนท้ายของ TH
rowIndex และ cellIndex ให้ตำแหน่งที่แน่นอนของเซลล์ที่ถูกคลิก
Textcontent เป็นข้อความของเซลล์ที่คลิก

ด้วยวิธีนี้ฉันไม่จำเป็นต้องส่งข้อมูลของเซลล์ไปยังตัวจัดการมันสามารถให้บริการด้วยตนเองได้
หากฉันต้องการข้อมูลเพิ่มเติมข้อมูลที่ไม่แสดงฉันสามารถใช้แอตทริบิวต์ชุดข้อมูลหรือองค์ประกอบที่ซ่อนอยู่
ด้วยการนำทาง DOM แบบง่าย ๆ ก็พร้อมใช้งานแล้ว
สิ่งนี้ถูกนำมาใช้ใน HTML นับตั้งแต่นั้นมาเนื่องจากพีซีได้ง่ายขึ้นมากที่จะชะงักงัน


0

มีสองวิธีในการส่งผ่านพารามิเตอร์ในตัวจัดการเหตุการณ์บางวิธีกำลังติดตาม

คุณสามารถใช้ฟังก์ชันลูกศรเพื่อล้อมรอบตัวจัดการเหตุการณ์และพารามิเตอร์ผ่าน:

<button onClick={() => this.handleClick(id)} />

ตัวอย่างข้างต้นเทียบเท่ากับการโทร.bindหรือคุณสามารถโทรผูกได้อย่างชัดเจน

<button onClick={this.handleClick.bind(this, id)} />

นอกเหนือจากวิธีการทั้งสองนี้คุณยังสามารถส่งผ่านข้อโต้แย้งไปยังฟังก์ชั่นที่กำหนดไว้เป็นฟังก์ชั่นแกง

handleClick = (id) => () => {
    console.log("Hello, your ticket number is", id)
};

<button onClick={this.handleClick(id)} />

-1

ใช้การปิดมันส่งผลให้วิธีการแก้ปัญหาที่สะอาด:

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

ฟังก์ชัน handleSort จะส่งคืนฟังก์ชันที่มีคอลัมน์ค่าที่ตั้งค่าไว้แล้ว

handleSort: function(value) { 
    return () => {
        console.log(value);
    }
}

ฟังก์ชั่นที่ไม่ระบุชื่อจะถูกเรียกด้วยค่าที่ถูกต้องเมื่อผู้ใช้คลิกที่ th

ตัวอย่าง: https://stackblitz.com/edit/react-pass-parameters-example


-2

ต้องการการเปลี่ยนแปลงอย่างง่าย:

ใช้ <th value={column} onClick={that.handleSort} >{column}</th>

แทน <th value={column} onClick={that.handleSort} >{column}</th>

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.