ตอบสนอง JS onClick ตัวจัดการเหตุการณ์


120

ฉันมี

var TestApp = React.createClass({
      getComponent: function(){
          console.log(this.props);
      },
      render: function(){
        return(
             <div>
             <ul>
                <li onClick={this.getComponent}>Component 1</li>
             </ul>
             </div>
        );
      }
});
React.renderComponent(<TestApp />, document.body);

ฉันต้องการระบายสีพื้นหลังขององค์ประกอบรายการที่คลิก ฉันจะทำสิ่งนี้ใน React ได้อย่างไร?

สิ่งที่ต้องการ

$('li').on('click', function(){
    $(this).css({'background-color': '#ccc'});
});

คำตอบ:


95

ทำไมจะไม่ล่ะ:

onItemClick: function (event) {

    event.currentTarget.style.backgroundColor = '#ccc';

},

render: function() {
    return (
        <div>
            <ul>
                <li onClick={this.onItemClick}>Component 1</li>
            </ul>
        </div>
    );
}

และถ้าคุณต้องการ React-ive มากขึ้นคุณอาจต้องการตั้งค่ารายการที่เลือกเป็นสถานะของส่วนประกอบ React จากนั้นอ้างอิงสถานะนั้นเพื่อกำหนดสีของรายการภายในrender:

onItemClick: function (event) {

    this.setState({ selectedItem: event.currentTarget.dataset.id });
    //where 'id' =  whatever suffix you give the data-* li attribute
},

render: function() {
    return (
        <div>
            <ul>
                <li onClick={this.onItemClick} data-id="1" className={this.state.selectedItem == 1 ? "on" : "off"}>Component 1</li>
                <li onClick={this.onItemClick} data-id="2" className={this.state.selectedItem == 2 ? "on" : "off"}>Component 2</li>
                <li onClick={this.onItemClick} data-id="3" className={this.state.selectedItem == 3 ? "on" : "off"}>Component 3</li>
            </ul>
        </div>
    );
},

คุณต้องการที่จะนำผู้ที่<li>s เข้าไปในห่วงและคุณต้องทำให้li.onและรูปแบบการตั้งค่าของคุณli.offbackground-color


การจัดการ DOM ด้วยตนเองใน React เป็นการต่อต้านรูปแบบซึ่งนำไปสู่ปัญหามากขึ้นเท่านั้น หลีกเลี่ยงสิ่งต่างๆเช่นevent.currentTarget.style.backgroundColor = '#ccc';เว้นแต่คุณจะเข้าใจจริงๆว่าคุณกำลังทำอะไรอยู่ (ส่วนใหญ่ในขณะที่ผสานรวมวิดเจ็ตของบุคคลที่สาม)
Emile Bergeron

61

ฉันคิดได้สองวิธีคือ

var TestApp = React.createClass({
    getComponent: function(index) {
        $(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
            'background-color': '#ccc'
        });
    },
    render: function() {
        return (
            <div>
              <ul>
                <li onClick={this.getComponent.bind(this, 1)}>Component 1</li>
                <li onClick={this.getComponent.bind(this, 2)}>Component 2</li>
                <li onClick={this.getComponent.bind(this, 3)}>Component 3</li>
              </ul>
            </div>
        );
    }
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));

นี่เป็นรายการโปรดส่วนตัวของฉัน

var ListItem = React.createClass({
    getInitialState: function() {
        return {
            isSelected: false
        };
    },
    handleClick: function() {
        this.setState({
            isSelected: true
        })
    },
    render: function() {
        var isSelected = this.state.isSelected;
        var style = {
            'background-color': ''
        };
        if (isSelected) {
            style = {
                'background-color': '#ccc'
            };
        }
        return (
            <li onClick={this.handleClick} style={style}>{this.props.content}</li>
        );
    }
});

var TestApp2 = React.createClass({
    getComponent: function(index) {
        $(this.getDOMNode()).find('li:nth-child(' + index + ')').css({
            'background-color': '#ccc'
        });
    },
    render: function() {
        return (
            <div>
             <ul>
              <ListItem content="Component 1" />
              <ListItem content="Component 2" />
              <ListItem content="Component 3" />
             </ul>
            </div>
        );
    }
});
React.renderComponent(<TestApp2 /> , document.getElementById('soln2'));

นี่คือการสาธิต

ฉันหวังว่านี่จะช่วยได้.


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

1
@ jony89 ตกลงถ้า.bindไม่ใช้พารามิเตอร์เพิ่มเติม แต่ในกรณีแรกจะเป็นเช่นนั้น ฉันไม่คิดว่าจะมีวิธีอื่น
Dhiraj

1
มีการสร้างฟังก์ชันที่แตกต่างกันสามฟังก์ชัน (ซึ่งสร้างขึ้นโดยผลลัพธ์ของ getComponent.bind (นี่คือ 1)) แม้ว่าการตัดสินใจนั้นจะเป็นการตัดสินใจที่แน่นอน (จะทำสำหรับ 2-3 องค์ประกอบไม่ใช่สำหรับ 20 - เว้นแต่จะเป็นปัญหาด้านประสิทธิภาพจริงๆ และง่ายต่อการสร้างแบบไดนามิก)
jony89

38

นี่คือวิธีที่คุณกำหนดตัวจัดการเหตุการณ์ react onClickซึ่งกำลังตอบคำถาม ... โดยใช้ไวยากรณ์ es6

import React, { Component } from 'react';

export default class Test extends Component {
  handleClick(e) {
    e.preventDefault()
    console.log(e.target)
  }

  render() {
    return (
      <a href='#' onClick={e => this.handleClick(e)}>click me</a>
    )
  }
}

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

18

ใช้ ECMA2015 ฟังก์ชันลูกศรทำให้ "สิ่งนี้" ใช้งานง่ายขึ้นมาก

import React from 'react';


class TestApp extends React.Component {
   getComponent(e, index) {
       $(e.target).css({
           'background-color': '#ccc'
       });
   }
   render() {
       return (
           <div>
             <ul>
               <li onClick={(e) => this.getComponent(e, 1)}>Component 1</li>
               <li onClick={(e) => this.getComponent(e, 2)}>Component 2</li>
               <li onClick={(e) => this.getComponent(e, 3)}>Component 3</li>
             </ul>
           </div>
       );
   }
});
React.renderComponent(<TestApp /> , document.getElementById('soln1'));`

2
indexไม่ได้ทำอะไรที่นี่?
อเมริกาเหนือ

@northamerican - ไม่เพียงแค่เพิ่มความชัดเจนของพารามิเตอร์เท่านั้น
itcropper

5
สิ่งนี้ไม่ดีต่อประสิทธิภาพเนื่องจากสร้างฟังก์ชันใหม่ในการแสดงผลแต่ละครั้ง ดู: stackoverflow.com/questions/36677733/…
Jochie Nabuurs

1
และโปรดอย่าใช้ jQuery ภายใน React ถ้าไม่ต้อง!
Emile Bergeron

13

หากคุณใช้ ES6 นี่คือตัวอย่างโค้ดง่ายๆ:

import React from 'wherever_react_is';

class TestApp extends React.Component {

  getComponent(event) {
      console.log('li item clicked!');
      event.currentTarget.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export default TestApp;

ในเนื้อความคลาส ES6 ฟังก์ชันไม่จำเป็นต้องใช้คีย์เวิร์ด 'function' อีกต่อไปและไม่จำเป็นต้องคั่นด้วยเครื่องหมายจุลภาค คุณยังสามารถใช้ไวยากรณ์ => ได้เช่นกันหากต้องการ

นี่คือตัวอย่างขององค์ประกอบที่สร้างขึ้นแบบไดนามิก:

import React from 'wherever_react_is';

class TestApp extends React.Component {

constructor(props) {
  super(props);

  this.state = {
    data: [
      {name: 'Name 1', id: 123},
      {name: 'Name 2', id: 456}
    ]
  }
}

  getComponent(event) {
      console.log('li item clicked!');
      event.currentTarget.style.backgroundColor = '#ccc';
  }

  render() {        
       <div>
         <ul>
         {this.state.data.map(d => {
           return(
              <li key={d.id} onClick={this.getComponent.bind(this)}>{d.name}</li>
           )}
         )}
         </ul>
       </div>
    );
  }
}

export default TestApp;

โปรดทราบว่าแต่ละองค์ประกอบที่สร้างแบบไดนามิกควรมี "คีย์" อ้างอิงที่ไม่ซ้ำกัน

นอกจากนี้หากคุณต้องการส่งผ่านออบเจ็กต์ข้อมูลจริง (แทนที่จะเป็นเหตุการณ์) ไปยังฟังก์ชัน onClick ของคุณคุณจะต้องส่งผ่านข้อมูลนั้นไปยังการผูกของคุณ ตัวอย่างเช่น:

ฟังก์ชัน onClick ใหม่:

getComponent(object) {
    console.log(object.name);
}

การส่งผ่านวัตถุข้อมูล:

{this.state.data.map(d => {
    return(
      <li key={d.id} onClick={this.getComponent.bind(this, d)}>{d.name}</li>
    )}
)}

ฉันพยายามสร้างรายการ li ของฉันแบบไดนามิกจากนั้นสิ่งนี้จะกลายเป็นไม่ได้กำหนดและฟังก์ชัน onClick จึงแสดงข้อผิดพลาด
ลงจอด

1
ฉันเพิ่งพบคำตอบที่คล้ายกันซึ่งคุณต้องใช้ .bind (this)); ในตอนท้ายของฟังก์ชั่นนิรนามตามที่นี่หมายถึงหน้าต่างจนกว่าคุณจะทำการผูก ...
ลงจอด


6

การจัดการเหตุการณ์ด้วยองค์ประกอบการตอบสนองนั้นคล้ายกับการจัดการเหตุการณ์บนองค์ประกอบ DOM มีความแตกต่างทางไวยากรณ์บางประการ:

  • เหตุการณ์การตอบสนองถูกตั้งชื่อโดยใช้ camelCase แทนที่จะเป็นตัวพิมพ์เล็ก
  • ด้วย JSX คุณจะส่งผ่านฟังก์ชันเป็นตัวจัดการเหตุการณ์แทนที่จะเป็นสตริง

ดังที่กล่าวไว้ในเอกสารReactจึงค่อนข้างคล้ายกับ HTML ปกติเมื่อพูดถึง Event Handling แต่ชื่อเหตุการณ์ใน React โดยใช้ camelcase เนื่องจากไม่ใช่ HTML จริงๆจึงเป็น JavaScript และคุณส่งผ่านฟังก์ชันในขณะที่เราส่งการเรียกฟังก์ชัน ในรูปแบบสตริงสำหรับ HTML ซึ่งแตกต่างกัน แต่แนวคิดค่อนข้างคล้ายกัน ...

ดูตัวอย่างด้านล่างให้ความสนใจกับวิธีส่งผ่านเหตุการณ์ไปยังฟังก์ชัน:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

3

import React from 'react';

class MyComponent extends React.Component {

  getComponent(event) {
      event.target.style.backgroundColor = '#ccc';
      
      // or you can write
      //arguments[0].target.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export { MyComponent };  // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;


ดูเหมือนว่าโดยพื้นฐานแล้วจะเหมือนกับคำตอบ 11 จุดและฟื้นคืนชีพคำถามที่น่ารักหรือทำไม?
Dave Newton

2

class FrontendSkillList extends React.Component {
  constructor() {
    super();
    this.state = { selectedSkill: {} };
  }
  render() {
    return (
      <ul>
        {this.props.skills.map((skill, i) => (
            <li
              className={
                this.state.selectedSkill.id === skill.id ? "selected" : ""
              }
              onClick={this.selectSkill.bind(this, skill)}
              style={{ cursor: "pointer" }}
              key={skill.id}
            >
            {skill.name}
            </li>
        ))}
      </ul>
    );
  }

  selectSkill(selected) {
    if (selected.id !== this.state.selectedSkill.id) {
      this.setState({ selectedSkill: selected });
    } else {
      this.setState({ selectedSkill: {} });
    }
  }
}

const data = [
  { id: "1", name: "HTML5" },
  { id: "2", name: "CSS3" },
  { id: "3", name: "ES6 & ES7" }
];
const element = (
  <div>
    <h1>Frontend Skill List</h1>
    <FrontendSkillList skills={data} />
  </div>
);
ReactDOM.render(element, document.getElementById("root"));
.selected {
  background-color: rgba(217, 83, 79, 0.8);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="root"></div>

@ user544079 หวังว่าการสาธิตนี้จะช่วยได้ :) ขอแนะนำให้เปลี่ยนสีพื้นหลังโดยการสลับชื่อคลาส


2

import React from 'react';

class MyComponent extends React.Component {

  getComponent(event) {
      event.target.style.backgroundColor = '#ccc';
      
      // or you can write
      //arguments[0].target.style.backgroundColor = '#ccc';
  }

  render() {
    return(
       <div>
         <ul>
            <li onClick={this.getComponent.bind(this)}>Component 1</li>
         </ul>
       </div>
    );
  }
}

export { MyComponent };  // use this to be possible in future imports with {} like: import {MyComponent} from './MyComponent'
export default MyComponent;


คุณสามารถระบุบริบทเพิ่มเติมให้กับโค้ดนี้โดยอธิบายว่าจะแก้ไขปัญหาได้อย่างไร
MEDZ

1

คุณสามารถใช้เมธอด React.createClone สร้างองค์ประกอบของคุณมากกว่าสร้างโคลนของมัน ในระหว่างการสร้างโคลนคุณสามารถฉีดอุปกรณ์ประกอบฉาก ฉีด onClick: prop วิธีการเช่นนี้

{ onClick : () => this.changeColor(originalElement, index) }

เมธอด changeColor จะตั้งค่าสถานะด้วยรายการที่ซ้ำกันทำให้คุณสามารถตั้งค่าสีในกระบวนการได้

render()
  {
    return(
      <ul>

        {this.state.items.map((val, ind) => {
          let item = <li key={ind}>{val}</li>;
          let props = { 
            onClick: () => this.Click(item, ind),
            key : ind,
            ind
          }
          let clone = React.cloneElement(item, props, [val]);
          return clone;
        })}

      </ul>
    )
  }


การโคลนนิ่งไม่จำเป็นโดยสิ้นเชิง
Emile Bergeron

-17

นี่เป็นรูปแบบการตอบสนองที่ไม่ได้มาตรฐาน (แต่ไม่ใช่เรื่องแปลก) ที่ไม่ใช้ JSX แต่จะใส่ทุกอย่างไว้ในบรรทัดแทน นอกจากนี้ยังเป็น Coffeescript

'React-way' ในการทำสิ่งนี้จะเป็นไปตามสถานะของส่วนประกอบ:

( c = console.log.bind console)

mock_items: [
    {
        name: 'item_a'
        uid: shortid()
    }
    {
        name: 'item_b'
        uid: shortid()
    }
    {
        name: 'item_c'
        uid: shortid()
    }
]
getInitialState: ->
    lighted_item: null
render: ->
    div null,
        ul null,
            for item, idx in @mock_items
                uid = item.uid
                li
                    key: uid
                    onClick: do (idx, uid) =>
                        (e) =>
                            # justf to illustrate these are bound in closure by the do lambda,
                            c idx
                            c uid
                            @setState
                                lighted_item: uid
                    style:
                        cursor: 'pointer'
                        background: do (uid) =>
                            c @state.lighted_item
                            c 'and uid', uid
                            if @state.lighted_item is uid then 'magenta' else 'chartreuse'
                        # background: 'chartreuse'
                    item.name

ตัวอย่างนี้ใช้ได้ - ฉันทดสอบในเครื่อง คุณสามารถตรวจสอบรหัสตัวอย่างนี้ตรงที่GitHub ของฉัน เดิม env เป็นเฉพาะสำหรับจุดประสงค์ r & d ไวท์บอร์ดของฉันเอง แต่ฉันโพสต์ไปที่ Github สำหรับสิ่งนี้ อาจมีการเขียนทับในบางจุด แต่คุณสามารถตรวจสอบข้อผูกพันได้ตั้งแต่วันที่ 8 กันยายน 2016 เพื่อดูสิ่งนี้

โดยทั่วไปถ้าคุณต้องการที่จะเห็นว่ารูปแบบนี้ซีเอส / ไม่มี JSX สำหรับตอบสนองการทำงานตรวจสอบบางส่วนผลงานล่าสุดที่นี่ เป็นไปได้ว่าฉันจะมีเวลาใช้ POC อย่างเต็มที่สำหรับแนวคิดแอปนี้สแต็กซึ่งรวมถึง NodeJS, Primus, Redis และ React


พื้นหลังไม่จำเป็นต้องเป็นdoแลมด้า: นิพจน์นี้ใช้ได้เช่นกัน:background: if @state.lighted_item is uid then 'magenta' else 'chartreuse'
Wylie Kulik

สวัสดีฉันจะดู onclick บนคอนโซลของเบราว์เซอร์ได้อย่างไร
Muneem Habib

12
เหตุใดคุณจึงใช้ CoffeeScript ในการตอบคำถามที่ไม่ได้กล่าวถึงในทางใดทางหนึ่ง มันไม่สมเหตุสมผลและอาจทำให้คำตอบอ่านยากขึ้นสำหรับผู้ถามเนื่องจากเขาอาจไม่รู้ / ชอบ CoffeeScript เห็นได้ชัดว่าการลงคะแนน
macbem

7
ไม่ใช่ แต่เป็นสิ่งที่สร้างขึ้นจากภาษานั้นเป็นสิ่งที่ไม่ได้มาตรฐานและต้องมีการติดตั้งและรวบรวม เป็นตัวเลือกที่ไม่ดีอย่างแท้จริงในการเขียนคำตอบของคุณในรูปกาแฟเมื่อมี ZERO บอกเป็นนัย ๆ ว่าพวกเขากำลังใช้ Coffeescript ในโครงการของพวกเขา
TheRealMrCrowley

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